トランザクションレス、完璧なCDC(Change Data Capture)システム構築法
2024年11月22日
はじめに
個人情報保護法、電子金融取引法、医療記録個人情報保護法など、個人情報を含む機密情報の変更履歴を記録する義務をサービス提供者に課す規制や法律は、日々強化されています。これらの法律が明示する義務事項に加えて、リアルタイムモニタリングや脅威検知、データ整合性の維持、事故対応など、サービスの維持管理やセキュリティにおいても、データベース変更履歴の記録は開発者、データベース管理者、セキュリティ専門家にとって見逃せない重要な課題です。
多くのサービス提供者は、このような要件を満たすために、データベースで発生するデータ変更をリアルタイムで識別し追跡する技術であるCDC(Change Data Capture)を活用しています。データベースアクセス制御機能を提供するQueryPieも、CDCをサポートしています。本ホワイトペーパーでは、QueryPieがどのような方法でトランザクションを使用せずにCDCシステムを実現したのかについて説明します。
課題
トランザクションベースCDCの課題
CDC(Change Data Capture)は、データベースの変更前後のデータを記録する必要があります。この機能を実現する方法の1つとして、データベースのトランザクションを利用する方法があります。
例えば、ユーザーがUPDATEクエリでデータを変更する場合、以下のようなプロセスを経て、変更前後のデータを記録します。

しかし、トランザクションを使用してCDCを実装する場合、ユーザーが対象テーブルに対して変更クエリを実行するたびに、トランザクションのロールバックを行う必要があります。このロールバック処理はDBMSに負荷をかけ、対象テーブルのサイズが大きくなるほどその負荷も増加します。さらに、同じテーブルに対してクエリの実行前後に2回の重複したデータ取得を行う必要があるため、データベースアクセスの効率が低下します。また、NoSQLの場合、トランザクションをサポートしていないか、サポートしていても限定的なレベルでしかトランザクションを提供していないため、トランザクションベースのCDCを実装するのは困難です。
トランザクションのロールバックがどの程度影響を与えるかは、簡単なテストシナリオを通じて確認できます。テストシナリオの環境は以下の通りです。:
MySQL 8.0 (オンプレミス、8コア(vcore 16)、メモリ256GB)
100,000件のレコード
以下のDDLを持つ
actorテーブルネットワークのボトルネックを無視できるほど高速な環境
CREATE TABLE actor ( actor_id int NOT NULL AUTO_INCREMENT, first_name varchar(255) NOT NULL, last_name varchar(255) NOT NULL, last_update timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (actor_id));このテーブルのすべてのレコードに対して、first_nameをすべて'Christopher'に変更するシナリオを想定してみます。トランザクションのロールバック機能を使用する場合、以下のようなクエリをすべて実行する必要があります。
mysql> SET AUTOCOMMIT=0;Query OK, 0 rows affected (0.06 sec) mysql> SELECT * FROM actor;...100000 rows in set (0.42 sec) mysql> UPDATE actor4 SET first_name='Christopher' WHERE 1;Query OK, 100000 rows affected (1.54 sec)Rows matched: 100000 Changed: 100000 Warnings: 0 mysql> ROLLBACK;Query OK, 0 rows affected (0.85 sec) mysql> UPDATE actor4 SET first_name='Christopher' WHERE 1;Query OK, 100000 rows affected (1.58 sec)Rows matched: 100000 Changed: 100000 Warnings: 0 mysql> COMMIT;Query OK, 0 rows affected (0.04 sec)動作に要する時間を表に簡略化すると、以下の通りです。実際の動作に必要なテーブルの参照および更新以外に、CDCのために追加された動作は黄色で強調されています。
動作 | 所要時間 |
|---|---|
テーブルクエリ | 0.42s |
テーブル更新 | 1.54s |
トランザクションロールバック | 0.85s |
テーブル更新 | 1.58s |
トランザクションコミット | 0.04s |
合計所要時間 | 4.43s |
ユーザークエリ動作の合計所要時間 | 2.04s |
追加された動作の合計所要時間 | 2.39s |
ご覧の通り、トランザクションロールバックに基づくCDCの場合、更新とロールバックにより所要時間が2倍以上に増加することが確認できます。つまり、ユーザー視点では、CDCを使用することで50%のパフォーマンス低下を経験することになります。
その他のCDC実装が抱える問題点
さらに、テーブルにトリガーを作成してデータの変更を追跡する方法、modified_atなどの変更時刻やタイムスタンプを記録し、定期的にクエリを実行する方法、あるいはMySQLのbinlogのようなログファイルを活用してCDCを実現する方法もあります。それぞれの実装には長所と短所がありますが、共通してCDCの動作のためにDBMSを変更する必要があり、CDCの動作がDBMSに依存するという欠点を共有しています。つまり、新たにDBMSインスタンスを追加するたびに、関連する設定を追加する必要があるということです。
では、DBMSに負荷をかけず、DBMSへの依存性を排除しながら効果的にCDCを実現するには、どのような方法を採用すればよいのでしょうか?
目標設定
DBMSに負荷をかけないためには、CDCを実装する際にトランザクションを使用せず、同時に同一テーブルへの重複したクエリを実行しない必要があります。また、DBMSとの依存性を防ぐため、CDCの動作のためにDBMSを改変することも避けるべきです。
QueryPieではこれらの要件を満たすため、クエリ実行後のデータを直接データベースから取得するのではなく、社内クエリ分析ライブラリであるQSI(Query Structure Interface)を活用してデータを取得しています。では、テーブルを直接参照することなく、どのようにしてクエリ実行後のデータを取得するのでしょうか?
ソリューション概要

QSIは、テーブルを直接参照するのではなく、ユーザーが実行するクエリがテーブルに加える変更をシミュレートし、変更後のテーブル結果を提供します。つまり、クエリ実行結果の確認をデータベースに依存させるのではなく、QSI内で直接クエリをシミュレートし、その結果を返す仕組みです。QueryPieのクエリシミュレーション技術を活用することで、CDCを利用してもデータベースでのトランザクション実行やロールバックは発生しません。また、必要なのはクエリの実行のみで、DBMSの修正は求められません。そのため、QueryPieをインストールした直後からすぐに利用を開始することができます。