Flink CDC コンポーネント
最近リアルタイムデータパイプラインを整理する中で、Flink CDC に関する知識をあらためてまとめ直しました。「データベースの変更をリアルタイムに外へ運び出す」というニーズはほぼすべてのデータチームが直面するものなので、独立した記事として記録しておく価値があると思います。
背景
データ同期をやろうとしたとき、最も素朴な方法は定時バッチです。毎日深夜に業務 DB からフルダンプを取り、データウェアハウスに書き込む。実装はシンプルですが、遅延は時間単位、下手をすると日単位になり、データ量が増えるとソース DB への負荷も無視できません。ビジネス側から「レポートはリアルタイムで見たい」という要望が出た瞬間、バッチ方式は破綻します。CDC はまさにこの問題を解決するために登場した技術路線であり、Flink CDC は CDC の能力とストリーム処理エンジンをかなり徹底的に統合した実装の一つです。
Flink CDC(Change Data Capture)は Apache Flink エコシステムの重要なコンポーネントで、データベース内のデータ変更をリアルタイムにキャプチャし、それらの変更をデータストリームに変換して処理するために使われます。モダンなデータアーキテクチャにおいて、CDC 技術はリアルタイムデータ同期、リアルタイムデータウェアハウス、イベント駆動システムの構築に広く利用されています。
従来のデータ同期方式(定時のフル同期など)と比べ、CDC はデータベースの Binlog / WAL などの変更ログ を読み取ることでデータ変化をキャプチャし、低遅延・低侵襲のデータ同期を実現できます。
CDC とは
CDC(Change Data Capture)とは 変更データキャプチャ のことで、その核心となる考え方は次のとおりです。
データベース内のデータに INSERT / UPDATE / DELETE 操作が発生したとき、それらの変化を記録し、リアルタイムに下流システムへ伝達する。
ここで実装メカニズムを少し掘り下げてみます。CDC は大きく 2 種類に分かれます。1 つはクエリベースのもので、タイムスタンプや自動採番の主キー列をポーリングして新しいデータを発見します。実装は簡単ですが DELETE を捕捉できず、2 回のポーリングの間に複数回変更されたレコードを取りこぼしやすいという欠点があります。もう 1 つはログベースのもので、データベースがマスター・スレーブレプリケーションのために保持している変更ログを直接消費します。MySQL の Binlog、PostgreSQL の WAL がこれに該当します。ログには各行の変更前後の完全な状態が自然に記録されているため、ログベースの CDC は挿入・更新・削除のすべてのイベントを取得でき、しかもソース DB への追加のクエリ負荷はほとんどありません。Flink CDC が採っているのはこのログの路線です。
実際のデータアーキテクチャにおいて、CDC は次のような用途でよく使われます:
- データベース → データウェアハウス(リアルタイム DWH)
- データベース → Kafka(イベントストリーム)
- データベース → 検索エンジン(Elasticsearch など)
- データベース → キャッシュシステム(Redis など)
このように、CDC はモダンな リアルタイムデータプラットフォーム(Real-time Data Platform) の重要な基盤能力の一つです。
Flink CDC コンポーネントの動作原理は、ソースデータシステムの変更ログをキャプチャし、それをデータストリームに変換してリアルタイムに処理するというものです。CDC コンポーネントはソースシステムのログを読み取るだけで、ソースシステムへの書き込みは行わないため、このプロセスはソースシステムに影響を与えずに実行できます。データベースの視点から見ると、CDC クライアントは通常のスレーブと本質的な違いはありません。レプリケーションクライアントに擬態してマスターに変更ログを購読し、ログイベントを構造化された変更レコードに解析して Flink に渡します。
Flink CDC コンポーネントの利点は、リレーショナルデータベース(MySQL、Oracle など)と NoSQL データベース(MongoDB、Cassandra など)を含む多様なデータソースをサポートできることです。さらに、JSON、CSV など複数のデータフォーマットにも対応しています。
もう一つ過小評価されがちな利点は、Flink 本体との統合の深さです。変更ストリームが Flink に入ってしまえば、それは普通のデータストリームであり、Flink のウィンドウ計算、複数ストリームの Join、状態管理、checkpoint による耐障害機構をそのまま適用できます。つまり「変更のキャプチャ」と「変更の処理」が同一エンジン内で完結し、中継のために Kafka Connect クラスタを別途運用する必要がなく、パイプラインは短く、運用対象も少なくなります。
Flink CDC コンポーネントを使えば、リアルタイムのデータ同期とデータストリーム処理が実現できます。例えば、CDC コンポーネントで MySQL データベースのデータを Kafka に同期し、その後 Flink でリアルタイムのデータ処理を行うといった使い方ができます。こうしてデータのリアルタイム同期と処理が実現でき、データの効率と正確性が向上します。
Flink CDC コンポーネント
全体像として、Flink CDC パイプラインの分担はおおむね次のとおりです。Source が「入口」を、Sink が「出口」を担当し、その間を Flink のオペレーターと状態機構が計算で埋めます。Flink CDC には以下のコンポーネントが組み込まれています:
- Source:データソースからデータ変更ログを読み取り、Flink のデータストリームに変換するコンポーネントです。通常、CDC Source はまず既存データのスナップショット読み取りを行い、その後シームレスに増分ログの消費へ移行します。これにより下流は「フル + 増分」の完全なデータを受け取れます。
- Debezium Connector:オープンソースの CDC ツールで、複数のデータソース(MySQL、PostgreSQL、MongoDB など)に接続してデータ変更ログをキャプチャできます。Flink CDC は下層で Debezium のログ解析能力を再利用し、それを組み込み(embedded)方式で Flink ジョブ内に走らせることで、Debezium サービスを独立してデプロイするコストを省いています。
- Sink:Flink のデータストリームをターゲットデータソース、例えば Kafka、HDFS、Elasticsearch などに書き込むコンポーネントです。Sink は Flink の checkpoint 機構と連携し、ジョブが失敗して再起動した際のデータロスを防げます。
- State:ステートフルなデータストリームを処理するためのコンポーネントです。例えば 2 つのデータストリームをマージする場合、中間状態を保存するために State コンポーネントが必要になります。CDC のシナリオでは State がとりわけ重要です。例えばディメンションテーブルとの結合、主キーによる重複排除、UPDATE イベントから最新スナップショットの復元などは、いずれも状態ストレージに依存します。
- Table API:SQL ライクな API を提供するコンポーネントで、データストリームの処理とクエリを手軽に行えます。大半の同期系の要件は、Flink SQL で CDC ソーステーブルとターゲットテーブルを 1 つずつ宣言し、INSERT INTO を 1 文書くだけでパイプライン全体を動かせます。Java コードを書く必要はありません。
ハマりどころと注意点
実際の利用にあたって、事前に押さえておきたいポイントがいくつかあります:
-
ソース DB のログ設定を先に確認すること。MySQL を例にとると、Binlog が有効かつフォーマットが ROW である必要があります。そうでないと行レベルの変更前後のイメージが取得できません。接続アカウントにも Binlog 読み取りに関わるレプリケーション権限が必要です。
-
ログの保持期間を短くしすぎないこと。ジョブをしばらく停止してから再開したとき、その間の Binlog がすでにデータベースによって削除されていると、ジョブは再開位置を見つけられず、フルスナップショットをやり直すしかなくなります。
-
下流が UPDATE と DELETE を処理できること。CDC ストリームはリトラクト(回撤)のセマンティクスを含む変更ストリームです。Sink 側(例えば追記書き込みしかサポートしないストレージ)が削除や更新のイベントを消化できない場合、パイプライン設計で追加の対処が必要になります。
CDC ジョブは本質的に長時間走り続けるストリームジョブなので、checkpoint は必ずきちんと設定してください。checkpoint のない CDC ジョブは、一度失敗すると再開位置を失い、データを失う可能性もあります。
まとめ
CDC が解決するのは「データベース内の変化を、いかに低遅延・低侵襲で外に流すか」という問題であり、ログベースの実装が現在の主流路線です。Flink CDC の価値は、ログキャプチャ(Debezium の活用)とストリーム計算(Flink エンジン自体)を一つのフレームワークにまとめ上げたことにあります。Source が変更を読み、Sink がターゲットに書き、State がステートフルな計算を支え、Table API が利用の敷居を下げる。リアルタイム DWH を構築したい、あるいはヘテロジニアスなデータ同期をやりたいチームにとって、パイプラインが短く、コンポーネントが少なく、優先的に検討する価値のある選択肢です。
COMMENTS