オライリーから出た「大規模データ管理」という新刊を読んだ。
私の担当するような基幹系システムでも、特に業務は変わっていないと思うのだけど、扱うデータ量は日々増加しており、昨今の機械学習の進展やDXなる謎キーワードの登場でデータの重要性が叫ばれる中、その傾向には拍車がかかっている。
ということで、非常に興味を持って読み始めたのだけど、個人的には違和感を持ってしまった。誤解を招くと良くないが、トピックを網羅的に扱っている真面目な本で、決して悪い本ではない。*1
違和感の理由を考えてみたのだけど、ひとつは切り口が機能に寄りすぎてニーズがはっきりとしないところにあると思う。書いてないわけではないけれど、あくまで世の中にはこういう機能があってこう使うんですよ、という語り口になっている。
私もこの手のシステムを提案することはなくもないのだが、要件を確認するとそもそもなぜ必要なのかが不明確なことが多い。この本の中でもデータレイクの失敗率は6割にものぼる、と紹介されているが、あながち大げさとも言えない。課題がはっきりしないのに、新しいテクノロジーがあるから導入したところでうまくいくわけがない。
どのような切り口で書かれるべきだったか
私だったら、このお題でどういう構成にするだろうと考えてみた。
おそらく「必要とされるデータの鮮度」を軸にして書くと思う。この本で挙げられているようなRDS、API、ストリームアーキテクチャは、単に便利だからそれが選択されているわけでなく背景となる要因があったからこそ生まれたものだ。現実のデータ保管では、従来型のRDBやDWHでも十分な場合も多く、だからこそ何を理由にその技術を選択すべきか、という点が重要となる。
この手のプロジェクトでよく見られるのが、「すべてをリアルタイムにすればいいのでは」という発想だ。入力したらすぐに反映され、すぐに集計できる。リアルタイムはデータ処理の究極的な形であるわけだから、それを目指すべきであるというのは自然ではある。ところがこれはうまくいかない。
- すべてがリアルタイムということは、データのエントリーからデータ分析基盤までが一貫したトランザクションで制御されなければならない。蜜結合であるし、データ分析システムでの登録エラーが原因でエントリーのトランザクションがロールバックされるというのはどう考えて困った事態だ。
- リアルタイムという要件を緩めセミリアルタイムによる非同期処理にしたとしても、不具合があった場合への対応を考えると大きな困難を抱える。例えば、ストリーミングでのSQL集計を想定すると、不具合があった場合、該当のデータだけを再度ストリームに流すということは難しく正しい状態に戻すのは極めて難しい。従来的なバッチシステムならば単に再実行するだけで済む。
- そもそも経費精算のように人間が手入力するデータはリアルタイムには揃わない。不完全なデータはリアルタイムに入手できたところで意味がないだけでなく、誤った意思決定に繋がる危険性すらある。
ハードウェアにおいても「データの保持」という同じ目的のために、メインメモリやL1、L2といったキャッシュ、ディスク、テープといった様々な手段があり、それぞれ使い分けられている。なぜかと言えば、技術的なトレードオフがあるからだ。すべてを永続化可能なCPU直結のメモリにすればいい、というものではない。
データ管理においても同様だろう。私はデータの鮮度という観点で見るのがよいと思う。この観点だと、次のように分類できる。
- セッションや残在庫数など更新頻度が著しく高く、更新と参照の整合性が保たれる必要があり、その更新自体が事後的な分析には重要ではない場合、分散化されたNoSQLデータベースを選択すべき
- 具体的な実装方法: Redis、DynamoDB
- 理由:更新頻度が著しく高い=エンドユーザが頻繁に参照する、ことに他ならない。一方で更新頻度が高いので、その変化自体は事後の分析にはあまり重要ではない。高速でダウンタイムがない基盤の選択が必要となる。
- トランザクションやマスタの更新など、更新頻度は一般的なもので、更新と参照の整合性が保たれる必要があり、利用が特定のシステム内に完結する場合にはRDBを選択する
- 具体的な実装方法: MySQL、PostgreSQLなど
- 理由:運用がしやすく、技術的に枯れており長期的な維持がしやすい。NoSQLはデータの管理面ではまったく運用に適していない。
- もし、更新が頻繁に起こるためパフォーマンスに不安がある場合は、メッセージキューで処理することを検討すべきだろう。ただし、画面側からはポーリングを行い、キューが処理されるまで処理中であることを明示するなどしてユーザーに非同期処理であることは意識させるべきではない。
- マスタデータの配信など、1か所でデータを更新し複数のシステムに配信するようなもので、更新と参照の整合性が保たれる必要がある場合には WebAPI を選択する。
- ログの収集など、1か所でデータを集約する仕組みは WebAPI ではなく短周期の非同期的送信が望ましい。ストリーミングアーキテクチャはこのような用途に適している。
- 具体的な実装箇所: Fluentd、Kafka
- 理由:データ更新の仕組みを同期的に作ると SPOF となり、全システムが該当のAPIに依存してしまうため。
- 分析用データの収集は、この本でも書かれているように、システムごとに独立したソースデータ配置用ストアと、分析用のデータレイクにわけるべき。
- 具体的な実装方法: ソースデータ配置用ストア(S3など)→ ETL(Glueなど)→ データレイク(S3など)→ 分析ツール(Athena、Redshift、BigQueryなど)
- この本でも強調されているようにソースデータ配置用ストアに置かれたデータは他システムから参照できるようにしてはならない。フォーマットはCSVやJSON、固定長など多様でも構わない。
- 分析用データレイク上のフォーマットは Parquet に統一し Iceberg でテーブルを定義するような形で汎用的に構築するのがよい。特定の製品に依存すると将来的な移行が大変。
- この方式の問題は、RDBから分析用データをリアルタイムで配信したいという場合に、直接RDB to Redshift/BigQuery の方が多くのサポートが受けられるという点。理想論では直接はNGだが、コスト的にも実用的にも直結させた方が良い部分はどうしてもでてきてしまう。
【12/21追記】最近流行っている dbt というツールでは、ソースデータ配置用のストアも BigQuery などの DWH にしてしまえ、という発想らしい。DWH上の保管料金があまり変わらない かつ ソースデータの形式がDWHで直接取り込み可能なら、それも良いかなという気がする。
これはあくまで私見であることは断っておくけれども、ご意見があればぜひコメントに。
欠けている切り口
もうひとつ、この本には欠けているところがある。そのため、この本で語られているようなアーキテクチャを実際に実現しようとするとうまく行かない可能性がある。
何が欠けているのか。それは、データの持ち方だ。
大規模データ管理の前提として、特にトランザクションデータは不変であることが求められる。そうでなければ、過去データも含めて再送信が必要となり連携対象データが大量になってしまう。(差分で送ることも考えられるが、オブジェクトストレージにおいて差分更新はできないため再作成が必要になる。RedShift や BigQuery といった BI データベースにおいても差分更新はパフォーマンスが極めて悪い。)
データが不変であるということは、送信側でいわゆる赤黒処理の実装が必要だということになる。
マスタデータに関しても、過去データを参照する場合、最新のマスタデータと突合するのでは不適切な場面が多々ある。特に組織の統廃合が起こる場合には期間情報を保持することで履歴管理を行いその当時の組織構造をわかるようにしておくか、トランザクションデータ側に情報をコピーしておかなければならない。
大規模データ管理を語る上で、データの持ち方もアーキテクチャと同じくらい重要だと思うのだが、いかがであろうか。
*1:Parquet や Iceberg に触れられていないのはちょっとどうかと思うけれども、原著が書かれた時期の問題かもしれない。