Canal コンポーネント
はじめに
業務システムを作っていると、こんな要件によく出会います。データベースのある行が変更されたら、下流のキャッシュ、検索エンジン、データウェアハウスも追従して変わってほしい。業務コードでのダブルライトは漏れやすく、定期タスクでの全量突き合わせは重すぎる。この種の問題には、よりエレガントな解法があります——データベースの変更ログを直接購読することです。これが、この Canal ノートを整理した理由でもあります。
Canal は Alibaba がオープンソースで公開している MySQL Binlog ベースのデータリアルタイム購読・消費コンポーネントで、データベースの変更データキャプチャ(CDC、Change Data Capture)の実現によく使われます。
実際の業務シナリオでは、データベース内のデータ変更は業務の読み書きだけに使われるとは限らず、他のシステムへの同期が必要になることもあります。例えば:
- リアルタイムデータ同期(データベース → データウェアハウス)
- データ変更の MQ へのプッシュ(Kafka / RocketMQ)
- リアルタイムデータ分析・監視システムの構築
- 検索エンジンへのデータ同期(Elasticsearch など)
これらのシナリオに共通するのは、下流システムが関心を持つのは「データベースが今どうなっているか」ではなく、「データにどんな変化が起きたか」だという点です。業務コードが書き込みのたびに手動で下流に通知する方式は、侵入性が高いだけでなく、データベーストランザクションとの整合性を保証するのが困難です——書き込みは成功したのに通知が失敗すれば、データはいつの間にか不整合になってしまいます。一方 Binlog は MySQL 自身が書き込みを保証するログであり、これをデータソースにすればダブルライト問題を自然に回避できます。
これらの機能を実現するために、Canal は MySQL Slave をシミュレートする方式で Binlog ログを購読し、データベースの追加・削除・更新操作を解析して、これらの変更データを下流の消費システムにリアルタイムでプッシュすることで、データの準リアルタイム同期を実現しています。
動作原理
Canal の実装方式は、MySQL のレプリケーション(主従複製)の仕組みを借用したものです。通常のレプリケーションのフローは次のとおりです。Slave が Master に dump リクエストを送り、Master が Binlog イベントを継続的に Slave にプッシュし、Slave がそれらのイベントをリプレイしてデータ同期を完了します。
Canal がやっているのは、自分自身を Slave に「偽装」することです:
- MySQL に dump プロトコルのリクエストを送信し、本物のレプリカと同じやり取りの方式を使う。
- MySQL が Binlog イベントを Canal にプッシュし、Canal はバイナリログを解析して、各行データの変更内容を復元する。
- 解析後の構造化イベントを下流の消費に渡す——クライアントが TCP で能動的にプルすることも、Kafka / RocketMQ などのメッセージキューに直接投入することもできます。
MySQL から見れば、Canal はごく普通のレプリカにすぎず、データベース側にプラグインを一切インストールする必要はありません。この方式の侵入性が低いのはそのためです。
Canal が正常に購読できるようにするには、MySQL 側でいくつかの前提条件を満たす必要があります:
# my.cnf の重要な設定
log-bin=mysql-bin # Binlog を有効化
binlog-format=ROW # ROW モード必須。各行の変更前後の完全なデータを取得するため
server_id=1 # レプリケーション体系内の各ノードの一意な識別子。Canal 側に設定する slaveId と衝突しないこと
このうち Binlog のフォーマットは必ず ROW モードでなければなりません。STATEMENT モードで記録されるのは SQL 文そのものであり、各行データの具体的な変化を復元できません。ROW モードだけが行レベルの前後イメージを記録するため、CDC のシナリオで意味を持ちます。また、Canal が MySQL への接続に使うアカウントには REPLICATION SLAVE と REPLICATION CLIENT の権限が必要で、これは本物のレプリカの要件と同じです。
よくある CDC コンポーネントの比較
現在、市場でよく見かける CDC コンポーネントには主に次のものがあります:
- Canal
- Debezium
- Flink CDC
Canal が現在サポートしているのは MySQL データベースのみです。5.x、8.x バージョンに対応しています。
GitHub - alibaba/canal: 阿里巴巴 MySQL binlog 增量订阅&消费组件
これらのコンポーネントのコアとなる動作メカニズムは基本的に同じで、データベースの Binlog ログを解析することで、データの変更内容と具体的な操作タイプ(INSERT / UPDATE / DELETE)を取得し、これらの変更データを構造化イベントに変換して下流システムに消費させます。
3 者の違いは主にエコシステム上の位置付けにあります。Debezium は Kafka Connect の上に構築されており、MySQL、PostgreSQL など複数のデータベースをサポートするため、すでに Kafka 体系を持つチームに向いています。Flink CDC は変更キャプチャを Flink のストリーム処理に直接組み込み、キャプチャと加工を同一ジョブ内で完結させます。Canal はより軽量で、MySQL という 1 つの方向に特化しています。
注意すべきは、Canal が現在主にサポートしているのは MySQL データベース(5.x と 8.x バージョン) だという点で、そのため MySQL エコシステムの中でリアルタイムデータ同期・データ配信システムの構築に広く使われています。技術スタックが純粋な MySQL で、Kafka Connect や Flink のような重い依存を持ち込みたくないなら、Canal は現実的な選択肢と言えます。
高可用性について
高可用性の面では、Canal はクラスターデプロイモードを提供しています。クラスターアーキテクチャでは、各 Service ノードが複数の同期タスクインスタンス(Instance)を管理し、タスク分配メカニズムによって負荷分散を実現します。
クラスターモードは調整役として ZooKeeper に依存します。複数の Server ノードが同一 Instance の実行権を奪い合い、同時刻に実際にその Instance を実行しているのは 1 ノードだけで、残りのノードは standby 状態にあります。実行中のノードに障害が発生すると、standby ノードが ZooKeeper を通じてそれを検知してタスクを引き継ぎます。同時に、消費ポジション(つまり Binlog の消費進捗)も ZooKeeper に記録されているため、引き継いだ側は前回のポジションから消費を続行でき、データの損失や大量の重複を回避できます。
ただし実際の運用では、ネットワークの揺らぎ、データベース接続の異常、リソース制限などが原因で、同期タスクのインスタンスが停止してしまうことが時折あります。そのため本番環境では、Canal は通常自動再起動メカニズムや運用監視システムと組み合わせて、異常時にタスクが自動的に復旧できるようにします。
ハマりどころと注意点
原理を踏まえて、導入前に考えておいた方がよいポイントがいくつかあります。
-
Binlog のフォーマットと保持期間。上流のデータベースは必ず ROW モードにすること。同時に Binlog の保持期間を十分長くしておくこと。そうしないと、Canal の停止時間が長引いた場合にポジションに対応するログがすでに削除されており、タスクを続行できず、再初期化するしかなくなります。
-
消費のセマンティクスは at-least-once。フェイルオーバーや再起動の後、変更イベントの一部区間が重複して配信される可能性があります。下流の消費側は主キーで冪等処理を行う必要があり、各変更が一度しか届かないと仮定してはいけません。
-
順序性はパーティション戦略に依存する。MQ に投入する際、複数のテーブルや複数の主キーの変更が異なるパーティションに分散されると、消費順序が Binlog の順序と一致しなくなる可能性があります。順序に敏感なシナリオでは、通常テーブル名や主キーでパーティションルーティングを行います。
-
Instance の疑似停止(ハングアップ)問題。タスクインスタンスは必ずしもきれいに終了するとは限らず、あるポジションで止まったまま進まなくなることがあります。監視ではプロセスの生存だけを見るのではなく、ポジションが継続的に前進しているか、データベースの現在の Binlog 位置との遅延がどれくらいかも見る必要があります。
リリース前に一度「切断訓練」をしておくとよいでしょう。実行中の Server ノードを手動で kill し、standby が期待どおりに引き継ぐか、ポジションが正しく続行されるかを観察します。障害が起きてから検証するより、はるかに余裕を持って対応できます。
まとめ
Canal の考え方は複雑ではありません。MySQL のレプリカに偽装し、標準のレプリケーションプロトコルで Binlog を購読し、行レベルの変更を構造化イベントに復元して下流に消費させる。データベースへの侵入はゼロで、デプロイも軽量なため、純粋な MySQL 技術スタックでのリアルタイム同期・データ配信に適しています。エンジニアリング面で重点的にケアすべきなのは、ROW フォーマットと Binlog の保持ポリシー、下流の冪等な消費、そしてポジションの進行状況に基づく監視です——これらをしっかり作り込んでこそ、パイプラインの安定性が保証されます。
COMMENTS