1. はじめに
前の記事「ClickPipes CDC の取り込み上限を実測する」で、PostgreSQL → ClickHouse Cloud の ClickPipes CDC は、書き込みをいくら増やしても単一パイプの持続的な取り込みが約 4〜5 万行/秒(約 13〜14 MB/秒)で頭打ちになることを実測しました。そのとき ClickHouse 側の CPU には余裕が残っており、ボトルネックはパイプ側にあると結論づけています。
一方で、ClickHouse 公式ブログ Postgres CDC in ClickHouse, A year in review にはこんな一文があります。
performance enhancements such as faster ingestion into ClickHouse through chunking and parallel replicas
「parallel replicas で取り込みが速くなる」— それなら、ClickHouse のレプリカを増やせば前回の上限は動くのでしょうか? 今回はパイプを 1 本のまま、ClickHouse Cloud のレプリカ数だけを 1 → 2 → 3 と増やして、初期ロード後の通常運転(以下「継続 CDC」)と初期ロード(バックフィル)の両方で実測しました。
1.1. 今回の検証ゴール
| # | ゴール |
|---|---|
| G1 | 継続 CDC の持続的な取り込みレートが、レプリカ 1/2/3 でどう変わるかを実測する |
| G2 | ボトルネックがパイプ側か ClickHouse 側かを、取り込みレート・スロットのラグ・レプリカ別 CPU で切り分ける |
| G3 | 公式の「parallel replicas で取り込み高速化」が本構成で再現するかを確認する |
1.2. 結論の先出し
- 継続 CDC の持続的な取り込みレートは、レプリカ 1/2/3 のどれでも 約 40,000 行/秒のまま変わらない
- 1 億行(26 GB)のバックフィルも 約 27 万行/秒(±2%)でレプリカ数の影響なし
- 両経路とも ClickHouse の CPU はレプリカあたり 1 コア未満しか使われず、ボトルネックはパイプ側にある
- つまりこの構成では、取り込みを ClickHouse 側のスケールで速くすることはできない。一方、同じテーブルでもバックフィル経路は継続 CDC の約 7 倍速い
- そもそも持続 約 40,000 行/秒は 1 日あたり約 34.6 億行に相当し、これで足りる用途が大半と考えられる。超えても壊れず、ピークが過ぎれば取り込みは追いつく(6.3 章)
2. 検証環境
| 項目 | 値 |
|---|---|
| ソース DB | Managed Postgres r8gd.4xlarge 相当(16 vCPU)、PostgreSQL 18.4、wal_level=logical
|
| CDC | ClickPipes Postgres CDC(PeerDB ベース)、Pull batch size 100,000 / Sync interval 60 秒(デフォルト) |
| ターゲット | ClickHouse Cloud 26.2.1.428、1 レプリカ = 12 GiB / 3 vCPU |
| レプリカ数 | 1 → 2 → 3 に固定変更(オートスケール無効。本検証で変更するのはこの項目だけ) |
| 負荷クライアント | pgbench 17.10(前回記事と同一クライアント・同一スクリプト) |
| 対象テーブル |
cdc_probe(継続 CDC 用、payload 約 200 バイト/行)、cdc_backfill(バックフィル用、1 億行 / 26 GB、同じ行形状) |
レプリカ数は ClickHouse Cloud コンソールの Scaling 設定で固定します。たとえば 3 レプリカ時は「Your service will be pinned at 36 GiB, 9 vCPU」と表示され、オートスケールによる自動的な増減は起きません。
構成はこうなります。変更するのは ClickHouse のレプリカ数だけです。
3. 検証設計
3.1. 継続 CDC の「持続的な取り込みレート」の測り方
前回確認したとおり、書き込みレートがパイプの上限を超えるとスロットの未処理分(bytes_behind)が増え続け、書き込みを停止した後にパイプが残りを取り込み切る(以下「ドレイン」と呼びます)挙動になります。今回はこれを利用して、
- pgbench で最大負荷(バッチ 100 行 INSERT × 12 クライアント × 60 秒、約 29 万行/秒)をかけてスロットに数 GB の残りを作る
- 書き込みを停止した後のドレイン中に、ClickHouse 側の行数(count)の増分を 20 秒間隔で記録する
という方法で持続的な取り込みレートを測ります。ドレイン中はパイプが全力で取り込み続けるため、count の増分がそのままパイプの上限になります。レプリカ数だけを変更して同じ手順を繰り返せば、差分がレプリカ数の効果です。
3.2. バックフィルの測り方
バックフィル(初期ロード)は、1 億行 / 26 GB の cdc_backfill テーブルをパイプに追加して初期ロードを走らせ、ClickHouse 側 count を 15 秒間隔で記録します。比較指標は 2,000 万行 → 1 億行の線形区間の傾きに統一しました。開始直後はスナップショットの準備時間が混ざるため、立ち上がりを除いた区間で比べる方が正確です。
ClickHouse 側 CPU は、共有環境でも自レプリカ分だけを正しく測れる cgroup ベースのメトリクス(system.asynchronous_metrics の CGroupUserTime + CGroupSystemTime)を clusterAllReplicas() でレプリカ別に取得しました。
4. 継続 CDC はレプリカを増やしても約 40,000 行/秒のまま
4.1. 負荷中の挙動はレプリカ数によらず同一
まず最大負荷中(約 29 万行/秒の書き込み)の 3 指標です。レプリカを増やしても、スロットの溜まり方もレプリケーションラグもまったく同じカーブを描きます。
| レプリカ数 | 書き込み達成 行/秒 | スロット残の推移(60 秒間) | レプリケーションラグ |
|---|---|---|---|
| 1 | 293,864 | 1.45 → 2.72 GB | 13.5 → 26.3 秒 |
| 2 | 298,877 | 1.47 → 2.87 GB | 13.6 → 27.2 秒 |
| 3 | 287,030 | 1.37 → 2.71 GB | 13.3 → 26.5 秒 |
4.2. ドレイン中の持続取り込みレート(主結果)
書き込みを停止した後のドレイン中、ClickHouse の count は 20 秒ごとにちょうど 80 万行ずつ増えていきます。レプリカ 1 では 19 回連続でこの増分が完全に一定でした。そして 2 レプリカでも同じです。
| レプリカ数 | 持続取り込みレート | 相対比 | スロットのドレイン速度 | ClickHouse CPU(取り込み中) |
|---|---|---|---|---|
| 1 | 約 40,000 行/秒 | 1.00 | 約 14 MB/秒 | 平均 約 0.4 / 3 コア |
| 2 | 約 40,000 行/秒 | 1.00 | 約 14 MB/秒 | 両レプリカとも 0.05〜0.5 / 各 3 コア |
| 3 | 約 40,000 行/秒 | 1.00 | 約 14 MB/秒 | 3 台とも 0.05〜0.3 / 各 3 コア |
コンピュートを 3 倍(3 vCPU → 9 vCPU)にしても、取り込みレートはほぼ動きませんでした。そして取り込み中の ClickHouse CPU はどの構成でも 1 コア未満です。2 レプリカ目・3 レプリカ目は時折マージなどで動くものの、取り込みレートは伸びていません。
5. バックフィルもレプリカ数では変わらない(ただし CDC の 7 倍速い)
5.1. 1 億行の初期ロードをレプリカ数別に 3 回実測
cdc_backfill(1 億行 / 26 GB)をパイプの Table settings に追加すると初期ロードが始まります。
レプリカ数を変更して同じ初期ロードを 3 回実施した結果です(毎回 ClickHouse 側のテーブルを削除してから再追加。スナップショット設定はデフォルトのまま固定)。
| レプリカ数 | 実効ロードレート | 相対比 | 2,000 万 → 1 億行の所要 | ClickHouse CPU |
|---|---|---|---|---|
| 1 | 約 267,000 行/秒 | 1.00 | 294 秒 | 平均 約 0.8 / 3 コア |
| 2 | 約 272,000 行/秒 | 1.02 | 294 秒 | 合計 約 0.8 / 6 コア |
| 3 | 約 264,000 行/秒 | 0.99 | 311 秒 | 合計 約 1.5 / 9 コア |
±2% の範囲に収まっており、こちらもレプリカ数の効果はありません。15 秒あたりの count 増分はどの構成でも +420〜440 万行で一定でした。
5.2. バックフィルの中身は「並列カーソルフェッチ」
初期ロード中の PostgreSQL 側を pg_stat_activity で覗くと、実装が見えます。
active | FETCH 131072 FROM peerdb_cursor_5871569256310202258
active | FETCH 131072 FROM peerdb_cursor_4953187152160580124
idle in transaction | SELECT pg_export_snapshot()
pg_export_snapshot() でエクスポートした一貫性スナップショットを共有しながら、並列ワーカーがカーソルから 131,072 行ずつフェッチする方式です。この並列ワーカー数は ClickPipes 側の初期ロードの並列スレッド数の設定(デフォルト 4・テーブル単位)で決まるもので、今回は 3 回とも既定の 4 で固定しています。つまりバックフィルの取り込みレート(約 27 万行/秒)はこのパイプ側の並列フェッチで決まっており、ここでも ClickHouse 側の余力は使い切られていません。
注目したいのは、同じテーブル・同じ行形状でも、バックフィル経路は継続 CDC 適用(約 4 万行/秒)の約 7 倍速いことです。スナップショットの並列読み出しと、単一ストリームで逐次デコード・適用する CDC とでは、経路の構造がまったく違います。
6. 考察
6.1. ボトルネックは両経路ともパイプ側にある
継続 CDC・バックフィルのどちらでも、(a) レプリカ数を 3 倍にして取り込みレートが変わらず、(b) ClickHouse CPU はレプリカあたり 1 コア未満(継続 CDC で平均 0.4、バックフィルで 0.8 程度)でした。取り込み先に余力を足しても使われない以上、上限を決めているのは取り込み先ではなくパイプ側です。継続 CDC は単一レプリケーションスロットの逐次デコードと適用、バックフィルは並列 4 ワーカーのフェッチが、それぞれのレートを決めています。
6.2. CDC を速くする公式のスケール手段は、ClickHouse 側ではなくパイプ側にある
結論から言うと、冒頭で引用した「parallel replicas で取り込みが速くなる」は、本記事の構成(スナップショット並列デフォルト・26 GB テーブル・3 vCPU/レプリカ、2026 年 6 月時点)では確認できませんでした。では公式の言うスループット改善はどこで効くのでしょうか。
公式 Docs には ClickPipes のスケーリング という専用ページがあり、CDC インフラ(ClickPipes 自体)のコンピュートを OpenAPI 経由で 1〜32 コアに変更できることが案内されています(4 TB 超の初期ロードや多数パイプの同居が想定ケース。"Most users won't need this API" という注記つき)。つまり公式が用意しているスループットのスケール手段は、ClickHouse サービス側のレプリカ数ではなくパイプ側のコンピュート増強です。これは今回の実測(取り込み先に余力を残したままパイプ側で上限が決まる)と整合します。
したがって本記事の約 40,000 行/秒も「絶対的な上限」ではなく、ClickPipes がデフォルトコンピュートのときの値と読むのが正確です。ClickPipes のコンピュートを増強すれば持続レートが上がり、その先で初めて parallel replicas(取り込み先の並列性)が意味を持つ可能性があります。
6.3. そもそもこの上限は実務で問題になるのか
持続 約 40,000 行/秒は、1 日あたり約 34.6 億行に相当します。一般的な OLTP ワークロードの変更量であれば、この上限に常時張り付くケースはむしろ少数派と考えられます。また前回確認したとおり、上限を超えても壊れるわけではなく、スロットに溜まった分は負荷が下がれば取り込みが追いつきます。一時的なピークならレプリケーションラグが伸びるだけで自然回復するため、「持続的に 4 万行/秒を超える変更が流れ続ける」システムでなければ、この上限が実害になる場面は限られます。逆に言えば、バルク処理の流し込みや複数ソースの集約でこのレートを持続的に超える見込みがあるなら、CDC 経路ではなくバックフィル(再同期)やバルクロードの併用を設計に入れておくのが現実的です。
7. まとめ
「どこを増やせば速くなるのか」を整理します。
| やりたいこと | 効く手段 | 効かない手段 |
|---|---|---|
| 継続 CDC を速くする | ClickPipes 側コンピュートのスケーリング(公式 API・1〜32 コア。未検証) | ClickHouse レプリカ増、Pull batch size 増 |
| 初期ロード・再同期を速くする | スナップショットの並列スレッド数、ClickPipes 側コンピュート増(公式は 4 TB 超の初期ロード向けに案内) | ClickHouse レプリカ増(デフォルト並列では効かず) |
| 取り込みと OLAP の同居を安定させる | ClickHouse レプリカ増(取り込みではなくクエリ側の並列性に効く) | — |
ClickHouse のレプリカを増やしても CDC の取り込みは速くなりませんでした。ただしこれは「ClickHouse が遅い」のではなく、取り込み先が余力を残したままパイプ側で上限が決まっているということです。レプリカ増の使いどころは取り込みではなく、OLAP クエリの同時実行側にあると考えています。
参考
- Postgres CDC in ClickHouse, A year in review — chunking and parallel replicas の記述
- ClickHouse Cloud boosts performance with SharedMergeTree and Lightweight Updates — レプリカ追加で取り込みがスケールする仕組み(並列書き込み前提)
- ClickPipes のスケーリング — CDC コンピュートを OpenAPI で 1〜32 コアに変更できる公式手段
- ClickPipes for Postgres FAQ — スロットと取り込みの基本挙動
- Parallelized Initial Load for CDC-based streaming from Postgres — PeerDB の並列スナップショット
- 前回記事: ClickPipes CDC の取り込み上限を実測する

