PeerDBとは
公式ドキュメントの「What is PeerDB?」より一文引用します。
https://docs.peerdb.io/introduction
What is PeerDB?
At PeerDB, we are building a fast, simple and the most cost effective way to stream data from Postgres to Data Warehouses, Queues and Storage engines.
(PeerDBでは、Postgresからデータウェアハウス、キュー、ストレージエンジンへデータをストリーミングするための、高速でシンプルかつ最もコスト効率の高い方法を開発しています。)
上記公式ドキュメントの文言からわかるように、PeerDBは元々PostgreSQL向けに開発されていたETLツールですが、最近ではMySQLにも対応しています。 しかし、依然としてQuickstart GuideなどにPostgreSQL用手順は載っていても、MySQL用手順がないのが現状です。 そこで、今回は、MySQL用PeerDB Quickstart Guideと称して、チュートリアル的な内容の記事を用意しました。
なお、MySQLもサポートされていることが公式ドキュメントから確認できますので、安心して使えます。
https://docs.peerdb.io/features/supported-connectors
検証の目的
今回は、以下の構成でCDC(Change Data Capture)によるデータ同期の動作を検証しています。
-
MySQL: ソースデータベース
-
PeerDB: ETLツール
-
ClickHouse: ターゲットデータベース
検証の目的は、MySQLで発生したINSERT/UPDATE/DELETEの変更イベントが、PeerDBを通じてClickHouseにどのように反映されるかを確認することです。
バージョンは以下の通りです。
|
1 2 3 4 5 6 7 8 9 |
$ docker inspect peerdb-server | grep Image (...) "Image": "ghcr.io/peerdb-io/peerdb-server:stable-v0.36.19", $ docker exec -it mysql mysqld --version /usr/sbin/mysqld Ver 8.4.9 for Linux on x86_64 (MySQL Community Server - GPL) $ docker exec -it clickhouse clickhouse-server --version ClickHouse server version 26.5.1.882 (official build). |
検証環境の準備
まずはPeerDBを起動し、ClickHouseとMySQLを同じDockerネットワークに接続します。 ここではDockerを使って環境を構築しました。
PeerDBの起動
https://docs.peerdb.io/quickstart/sql-quickstart より抜粋
|
1 2 3 |
git clone --recursive https://github.com/PeerDB-io/peerdb.git cd peerdb docker compose up -d |
ClickHouseの起動
https://clickhouse.com/docs/jp/install/docker より抜粋
|
1 2 3 4 5 6 |
docker pull clickhouse/clickhouse-server docker run -d \ --name clickhouse \ --ulimit nofile=262144:262144 \ clickhouse/clickhouse-server |
PeerDBと同じDockerネットワークに接続します。
|
1 |
docker network connect peerdb_network clickhouse |
ClickHouse側では、PeerDBが書き込みできるように必要な権限を付与しています。
|
1 |
docker exec -it clickhouse clickhouse-client |
https://docs.peerdb.io/connect/clickhouse/clickhouse-cloud より抜粋
|
1 2 3 4 5 |
CREATE DATABASE peerdb; CREATE USER peerdb_user IDENTIFIED BY '<your_password>'; GRANT INSERT, SELECT, DROP, CREATE TABLE ON peerdb.* to peerdb_user; GRANT CREATE TEMPORARY TABLE, s3 on *.* to peerdb_user; GRANT ALTER ADD COLUMN ON peerdb.* to peerdb_user; |
MySQLの起動
次にMySQLを起動し、PeerDB用のユーザーを作成します。
PeerDBに正しくバイナリログを受け渡すためには、binlog_row_metadataシステム変数をFULLに設定する必要があります。そのため、カスタムmy.cnfを使用してMySQLコンテナを作成しました(その他の設定値はMySQLイメージのデフォルトを保持しています)。
|
1 2 3 4 5 6 7 8 9 10 11 |
[mysqld] host-cache-size=0 skip-name-resolve datadir=/var/lib/mysql socket=/var/lib/mysql/mysql.sock secure-file-priv=/var/lib/mysql-files user=mysql pid-file=/var/run/mysqld/mysqld.pid binlog_row_metadata=FULL # add |
https://dev.mysql.com/doc/refman/8.4/en/docker-mysql-getting-started.html より抜粋
|
1 2 3 4 5 6 7 |
docker pull container-registry.oracle.com/mysql/community-server:8.4 docker run -d \ --name=mysql \ -v ${PWD}/my.cnf:/etc/my.cnf \ -e MYSQL_ROOT_PASSWORD='<your_password>' \ container-registry.oracle.com/mysql/community-server:8.4 |
PeerDBと同じDockerネットワークに接続します。
|
1 |
docker network connect peerdb_network mysql |
MySQL側のユーザーは以下のように作成しました。
必要な権限については、公式ドキュメントの説明ページが見当たりませんでしたので、PostgreSQL用の権限を見様見真似で作ってみたり、エラーメッセージを見て追加したりしました。参考程度に留めていただければ幸いです。
-
REPLICATION SLAVE権限 -
REPLICATION CLIENT権限 -
performance_schemaへSELECT権限
|
1 |
docker exec -it mysql mysql -uroot -p |
|
1 2 3 4 5 6 7 8 |
mysql> CREATE USER 'peerdb_user'@'%' IDENTIFIED BY '<your_password>'; Query OK, 0 rows affected (0.01 sec) mysql> GRANT REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'peerdb_user'@'%'; Query OK, 0 rows affected (0.01 sec) mysql> GRANT SELECT ON performance_schema.* TO 'peerdb_user'@'%'; Query OK, 0 rows affected (0.00 sec) |
Peerの作成
Peerの作成(ClickHouse側)
ブラウザからlocalhost:3000にアクセスし、+ New PeerからClickHouse用の接続情報を登録します。

主要なパラメータ
|
項目 |
値 |
説明 |
|---|---|---|
|
Name |
clickhouse |
任意のPeer名 |
|
Host |
clickhouse |
ホスト名 |
|
Port |
9000 |
ポート |
|
User |
peerdb_user |
接続用ユーザ |
|
Password |
<your_password> |
接続用パスワード |
|
Database |
peerdb |
データベース |
|
Disable TLS? |
ON |
TLS使用有無 |
検証目的のため、TLS暗号化を有効化しないをONにして、ClickHouseの9000ポート(非TLS用)を指定しました。
Validateボタンを押してPeer is valid成功表示が出ればOKです。そのままCreate peerで作成を完了します。
Peerの作成(MySQL側)
ブラウザからlocalhost:3000にアクセスし、Peersタブの+ New PeerからMySQL用の接続情報を登録します。

主要なパラメータ
|
項目 |
値 |
説明 |
|---|---|---|
|
Name |
mysql |
任意のPeer名 |
|
Host |
mysql |
ホスト名 |
|
Port |
3306 |
ポート |
|
User |
peerdb_user |
接続用ユーザ |
|
Password |
<your_password> |
接続用パスワード |
|
Disable TLS? |
ON |
TLS使用有無 |
検証目的のため、TLS暗号化を有効化しないをONにしました。
Validateボタンを押してPeer is valid成功表示が出ればOKです。そのままCreate peerで作成を完了します。
テスト用テーブルの作成(MySQL側)
CDC の動作確認用に、シンプルなテーブルを作成します。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
mysql> CREATE DATABASE test; Query OK, 1 row affected (0.01 sec) mysql> USE test mysql> CREATE TABLE t1 (id int PRIMARY KEY, name VARCHAR(50)); Query OK, 0 rows affected (0.02 sec) mysql> SHOW CREATE TABLE t1\G *************************** 1. row *************************** Table: t1 Create Table: CREATE TABLE `t1` ( `id` int NOT NULL, `name` varchar(50) DEFAULT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci 1 row in set (0.00 sec) |
初期データも投入しておきます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
mysql> INSERT INTO t1 VALUES (1, 'MySQL'),(2, 'ClickHouse'), (3, 'PeerDB'); Query OK, 3 rows affected (0.02 sec) Records: 3 Duplicates: 0 Warnings: 0 mysql> SELECT * FROM t1; +----+------------+ | id | name | +----+------------+ | 1 | MySQL | | 2 | ClickHouse | | 3 | PeerDB | +----+------------+ 3 rows in set (0.00 sec) |
peerdb_userにテストスキーマへの参照権限を与えます。
|
1 2 |
mysql> GRANT SELECT ON test.* TO 'peerdb_user'@'%'; Query OK, 0 rows affected (0.01 sec) |
Mirrorの作成
ブラウザからlocalhost:3000にアクセスし、Mirrorsタブの+ New MirrorからCDC用のMirrorを作成します。

|
項目 |
値 |
説明 |
|---|---|---|
|
Mirror type |
CDC |
CDC, Query Replication, Xminの内から選択 |
|
Mirror Name |
mirror |
任意のMirror名 |
|
Source Peer |
mysql |
ソースPeer名を選択 |
|
Destination Peer |
clickhouse |
ターゲットPeer名を選択 |
Select tables to syncでは先ほど作成したテストテーブルtest.t1を選択します。

+ Create Mirrorで作成を完了します。
PeerDBで同期した結果
同期開始後、程なくしてMySQLのtest.t1がClickHouse側にpeerdb.test_t1として同期されていました。
以下の点に注意が必要です。
-
MySQL側のスキーマ構造がそのまま同期されない
-
ClickHouse側Peer作成時に指定したデータベースの中にテーブルが作成される
-
<データベース名_テーブル名>の名前でテーブルが作成される
|
1 2 |
23246955c403 :) USE peerdb (...) |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
23246955c403 :) SHOW CREATE TABLE test_t1\G SHOW CREATE TABLE test_t1 Query id: b563db29-8a95-4fdc-a50d-df29755f9ebd Row 1: ────── statement: CREATE TABLE peerdb.test_t1 ( `id` Int32, `name` String, `_peerdb_synced_at` DateTime64(9) DEFAULT now64(), `_peerdb_is_deleted` UInt8, `_peerdb_version` UInt64 ) ENGINE = ReplacingMergeTree(_peerdb_version) PRIMARY KEY (id) ORDER BY (id) SETTINGS index_granularity = 8192 1 row in set. Elapsed: 0.003 sec. |
テーブル定義を見るに、ClickHouseの独自エンジンであるReplacingMergeTreeエンジンで作成されていることが確認できます。
MySQLテーブル定義の既存列は自動的に型変換され、新たにPeerDB用やReplacingMergeTreeエンジン用に追加されたアンダースコア(_)から始まる列が3つあります。
|
列名 |
データ型 |
説明 |
|---|---|---|
|
|
|
intから自動型変換 |
|
|
|
varcharから自動型変換 |
|
|
|
行が同期された時刻 |
|
|
|
行の削除済みフラグ |
|
|
|
行のバージョン |
この時点で、ClickHouseにはMySQLのデータが反映されていることが確認できました。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
23246955c403 :) SELECT * FROM test_t1 ORDER BY id; SELECT * FROM test_t1 ORDER BY id ASC Query id: 446321db-e85e-424a-9a0a-f94a660231bd ┌─id─┬─name───────┬─────────────_peerdb_synced_at─┬─_peerdb_is_deleted─┬─────_peerdb_version─┐ 1. │ 1 │ MySQL │ 2026-06-09 06:50:58.187000000 │ 0 │ 1780987798090982599 │ 2. │ 2 │ ClickHouse │ 2026-06-09 06:50:58.187000000 │ 0 │ 1780987798090985537 │ 3. │ 3 │ PeerDB │ 2026-06-09 06:50:58.187000000 │ 0 │ 1780987798090987722 │ └────┴────────────┴───────────────────────────────┴────────────────────┴─────────────────────┘ 3 rows in set. Elapsed: 0.003 sec. |
CDCの検証
次にMySQL側でUPDATEとDELETEを実行し、その変化がClickHouseにどう届くかを確認します。
MySQL側で更新・削除を実行
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
mysql> UPDATE t1 SET name = 'Postgre' WHERE id = 2; Query OK, 1 row affected (0.01 sec) Rows matched: 1 Changed: 1 Warnings: 0 mysql> DELETE FROM t1 WHERE id = 3; Query OK, 1 row affected (0.00 sec) mysql> SELECT * FROM t1; +----+---------+ | id | name | +----+---------+ | 1 | MySQL | | 2 | Postgre | +----+---------+ 2 rows in set (0.00 sec) |
ClickHouse側で変更履歴を確認
PeerDBは変更をそのまま反映するのではなく、ReplacingMergeTreeを使って履歴を保持する形で同期しているようです。 そのため、ORDER BY _peerdb_synced_atで見ると、同一キーに対する複数の変更履歴が見えます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
23246955c403 :) SELECT * FROM test_t1 ORDER BY _peerdb_synced_at; SELECT * FROM test_t1 ORDER BY _peerdb_synced_at ASC Query id: 39234e06-4bb0-4d4a-af8f-afffcf31b89c ┌─id─┬─name───────┬─────────────_peerdb_synced_at─┬─_peerdb_is_deleted─┬─────_peerdb_version─┐ 1. │ 1 │ MySQL │ 2026-06-09 07:00:01.070000000 │ 0 │ 1780988340991817972 │ 2. │ 2 │ ClickHouse │ 2026-06-09 07:00:01.070000000 │ 0 │ 1780988340991822860 │ 3. │ 3 │ PeerDB │ 2026-06-09 07:00:01.070000000 │ 0 │ 1780988340991825900 │ 4. │ 2 │ Postgre │ 2026-06-09 07:01:41.415000000 │ 0 │ 1780988441330156415 │ 5. │ 3 │ PeerDB │ 2026-06-09 07:01:41.415000000 │ 1 │ 1780988449955294060 │ └────┴────────────┴───────────────────────────────┴────────────────────┴─────────────────────┘ 5 rows in set. Elapsed: 0.003 sec. |
出力を見ると、以下の変化が確認できます。
-
id = 2のname = Postgre行が追記されている -
id = 3の_peerdb_is_deleted = 1行が追記されている
上記からわかる通り、MySQL側のUPDATEやDELETEなどの更新系は全てINSERTに置き換わっています。 これは追記型(Append-Only)を前提とした更新処理であることが伺えます。
FINALを付けた場合の見え方
ReplacingMergeTreeのテーブルでは、FINALを付けることで最終的な状態を確認できます。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
23246955c403 :) SELECT * FROM test_t1 FINAL ORDER BY _peerdb_synced_at; SELECT * FROM test_t1 FINAL ORDER BY _peerdb_synced_at ASC Query id: 36c5ea3b-ef23-4094-8a0e-7e7f719383a4 ┌─id─┬─name────┬─────────────_peerdb_synced_at─┬─_peerdb_is_deleted─┬─────_peerdb_version─┐ 1. │ 1 │ MySQL │ 2026-06-09 07:00:01.070000000 │ 0 │ 1780988340991817972 │ 2. │ 2 │ Postgre │ 2026-06-09 07:01:41.415000000 │ 0 │ 1780988441330156415 │ 3. │ 3 │ PeerDB │ 2026-06-09 07:01:41.415000000 │ 1 │ 1780988449955294060 │ └────┴─────────┴───────────────────────────────┴────────────────────┴─────────────────────┘ 3 rows in set. Elapsed: 0.004 sec. |
結果は以下のようになり、最終状態としては
-
id = 1はそのまま -
id = 2は_peerdb_versionの新しいPostgre行のみが見える -
id = 3は_peerdb_versionの新しい_peerdb_is_deleted = 1行のみが見える
であることがわかります。
削除済みフラグが立っている行を除外したい場合はWHERE _peerdb_is_deleted = 0などの条件でSELECTすると良いかもしれませんね。
検証してわかったこと
今回の検証で、MySQLでもPeerDBを使ったCDCが問題なく動作することを確認できました。
特に印象的だったのは以下の点です。
-
MySQLの変更がClickHouseにほぼリアルタイムで反映される
-
<データベース名_テーブル名>の名前でテーブルが作成される
-
UPDATE/DELETEはINSERTとして追記型で処理される -
ReplacingMergeTreeはSELECT ... FINALを使うことで現在の最新状態を取得できるが、DELETEされた行は依然として見えてしまうためWHERE句による除外が必要
まとめ
PeerDBの公式ガイドはPostgreSQLを前提とした内容が中心で、MySQL向けの情報は多くありません。しかし、今回の検証ではMySQLでも問題なくCDCを動作させられることを確認できました。 ClickHouse側ではReplacingMergeTreeとバージョン列を使って変更履歴を扱っており、CDCの仕組みがイメージしやすい構成になっています。


