0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ClickPipes CDC に書き込みを増やして、ClickHouse の取り込み限界を試してみた

0
Last updated at Posted at 2026-06-10

1. はじめに

ClickHouse Managed Postgres と ClickHouse Cloud を ClickPipes CDC でつなぐ構成を、これまで何度か試してきました。CDC を触っていて気になっていたのが「どれくらい書き込めば、CDC 経由で下流の ClickHouse 側に効いてくるのか」です。軽い書き込みだと ClickHouse はまったくの無風で、効かせるにはかなりの書き込み負荷が必要になりそうでした。

そこで今回は、CDC 対象テーブルへ直接 INSERT/UPDATE を叩き込む 書き込み増幅ワークロードで、変更行レートを段階的に最大 28万行/秒まで引き上げて、どこで効いてくるのかを試してみました。狙いは一点です。

どれだけ書き込みレートを上げれば、CDC 経由で ClickHouse 側に負荷をかけられるのか?

1.1. 今回の検証ゴール

# 検証項目 OK 条件
G1 書き込みレートを上げたとき CDC のレプリケーションラグが伸び始める点を特定 変更行レートを 1k→28万 行/秒で振り、レプリケーションスロットのラグ(バイト)が増え続けるようになるレートを示す
G2 同じ取り込み負荷の下で ClickHouse の OLAP 応答が劣化するか 各段階で OLAP を並走計測し、負荷なし(P0)比で示す。parts・CH CPU もあわせて取得
G3 パイプ自体をチューニング(Pull batch size 拡大)すれば上限は上がるか batch size を 10倍にした A/B で取り込みレートを比較

1.2. 結論(先出し)

  • INSERT 負荷・UPDATE 負荷のどちらも、最大 28万行/秒まで ClickHouse の OLAP 応答はほとんど変わらず(重いクエリで 2.0 秒 → 最大 2.7 秒、約 1.3 倍にとどまる)、active parts も 4〜10 で急増せず、UPDATE 負荷後の FINAL クエリも 0.035 秒で返る
  • ボトルネックは ClickHouse の計算ではなく CDC パイプのスループット。持続的な取り込みの上限は 約 3〜5万行/秒(約 13 MB/秒の WAL 相当) で、これを超えるとレプリケーションスロットが GB 級に膨らみ、レプリケーションラグが際限なく伸びる
  • Pull batch size を 10倍(10万→100万行)にしても上限は動かない。バッチが大きくなった分だけ処理時間も比例して延びるだけで、これはデフォルトの CDC コンピュートでの単一ストリーム上限。引き上げるにはバッチではなく取り込み側コンピュートの増強が要る(公式の手段。本記事では未検証)

1.3. 検証環境

項目
Managed Postgres ClickHouse Managed Postgres、r8gd.4xlarge(16 vCPU / 128 GiB、No standby)、PostgreSQL 18.4
ClickHouse Cloud 1 replica / 12 GiB / 3 vCPU(オートスケール固定 12 GiB ⇄ 12 GiB)、ClickHouse 25.12.1
CDC ClickPipes Postgres CDC(PeerDB ベース)、Pull batch size 100,000 / Sync interval 60 秒
CDC 対象テーブル cdc_probeid bigint PK / ts_pg timestamptz / payload text
負荷ツール pgbench、書き込み増幅(INSERT 負荷 / UPDATE 負荷)
クライアント ローカル環境

ClickHouse 側は 意図的にミニマル構成のまま固定しています。小さい ClickHouse の方が低いレートで限界に達するため、現実的な負荷で「ClickHouse 側の効果」に到達できるという狙いです。さらに 1 replica に固定することで、CDC の取り込み(書き込み・マージ)と OLAP クエリが同じコンピュートを奪い合う状態を保ち、もし干渉が起きるなら見えるようにしています。

ClickHouse Cloud はオートスケールが効くと、負荷の最中にコンピュートがスケールアウトされるため、「固定 3 vCPU での限界点」が測れなくなります。今回はスケーリング設定を 12 GiB ⇄ 12 GiB(固定)にし、検証中ずっと 1 replica のままで推移する事を確認しています。

  • ClickHouse側
    ClickHouse Cloud のスケーリング設定。Current/Recommended とも 12 GiB・3 vCPU・1 replica、Scaling configuration は 12 GiB ⇄ 12 GiB の固定。Active replica count は検証中 1 のまま、スケーリングイベントなし

  • PostgreSQL側
    Managed Postgres のリソース。16 vCPU / r8gd.4xlarge / No standby / Version 18。最大負荷時でも CPU はピーク約 55%(一過性)で余裕がある


2. 検証スクリプトとワークロード

CDC 対象テーブルへ直接書き込む方式にしたのは、連続 INSERT が本質的にシーケンシャルで、中規模の Postgres でも 10万行/秒を軽く超えられるためです。複雑な OLTP を組むより、変更行を効率よく大量生成できます。

CDC 対象には、既に ClickPipes パイプに乗って同期済みだった cdc_probe テーブルを流用しました。id は主キー、ts_pg に書き込み時刻を入れておき、ClickHouse 側の最新 ts_pg と現在時刻の差でレプリケーションラグを測ります。

2.1. 書き込み増幅の 2 モード

モード 内容 狙い
A: INSERT 負荷 100行ずつのバッチ INSERT を連続投入(id はシーケンス採番) 純粋な書き込み負荷
B: UPDATE 負荷 ホット行集合(id 1〜2000)を範囲更新しバージョンを量産 ReplacingMergeTree のマージ・FINAL 圧。少ない行数で ClickHouse 側に負荷をかける狙い

ClickPipes は CDC を ReplacingMergeTree にマッピングするため、UPDATE は新しいバージョン行として追記されます。同じ行を何度も更新するほどバージョンが増えてマージが重くなるはずなので、ClickHouse 側の処理を重くするにはこちらが効くと見込んでいました。

-- モードA: INSERT 負荷(pgbench カスタムスクリプト, batch は -D で指定)
INSERT INTO cdc_probe (ts_pg, payload)
SELECT clock_timestamp(), repeat('x', 200)
FROM generate_series(1, :batch);
-- モードB: UPDATE 負荷(ホット行 id 1..2000 を範囲更新, span は -D で指定)
\set span :span
\set start random(1, 1901)
UPDATE cdc_probe
SET payload = repeat('y', 200), ts_pg = clock_timestamp()
WHERE id >= :start AND id < :start + :span;

2.2. 書き込みレート

各モードを 4 段階のレートで流しました。rows/sec は pgbench の tps × バッチ行数 で制御し、ClickHouse 側の行数増分でも裏取りしています。

段階 目標 rows/sec 狙い
T1 ~1,000 ごく軽い負荷(基準)
T2 ~10,000 ラグが出始めるか
T3 ~50,000 遅れ始めの入口
T4 最大(無制限・12 クライアント) パイプ/ClickHouse の限界点

2.3. 計測パターンとラグの見方

パターン 書き込み モード OLAP 並走
P0 なし あり(ClickHouse 基準値)
Px-A T1〜T4 INSERT 負荷 あり
Px-B T1〜T4 UPDATE 負荷 あり

OLAP には CHBenCHmark の分析クエリから代表 3 本(軽い Q6、中程度の Q10、最も重い Q21)を選び、各段階で実行時間を計測しました。これらは cdc_probe ではなく、別途用意した TPC-C 由来のテーブル(700万行規模)に対して走るため、「CDC の取り込み負荷が、無関係な OLAP クエリの足を引っ張るか(=ClickHouse のコンピュート共有による干渉)」を見ています。

CDC のラグは、「秒単位のデータ鮮度」を 1 つで返す公式メトリクスが見当たらないため、役割を分けて 3 つの信号を併用しました。

信号 取得元 何を見るか
スロットのラグ(バイト) pg_replication_slots + ClickPipes コンソール パイプが追いつけず WAL が溜まり始める点(遅れ始めを見る主指標
取り込みスループット ClickPipes コンソール(CDC Syncs / Rows synced) 実際にどれだけ捌けているか
レプリケーションラグ(秒) ClickHouse の now64() - max(ts_pg) 読者目線の「データが何秒古いか」

レプリケーションラグ(秒)は ClickPipes のバッチ間隔が下限になるため、低負荷では負荷に起因する変化が埋もれます。パイプが遅れ始めたかは、スロットのラグ(バイト)が増え続けるかを主に見ます。

ClickPipes の Advanced settings。Pull batch size 100,000 / Sync interval 60 秒。Parallel threads や Snapshot 系は初期ロード専用で、定常 CDC には効かない


3. 検証結果

3.1. 達成した書き込みレート

12 クライアントの無制限実行で、クライアント側は楽に 28万行/秒を生成しました。

段階 INSERT 負荷(行/秒) UPDATE 負荷(行/秒) データ量目安(MB/秒)
T1 998 987 約 0.2
T2 10,055 10,331 約 2
T3 50,182 49,814 約 10
T4 284,560 279,260 約 57

データ量目安は 1 行あたり約 200 バイト(payload)で換算したざっくり値です。Postgres 側で生成される WAL や CDC が運ぶ量は、付帯情報ぶんこれより大きくなります。

これらの数値は「1 トランザクション 100 行・1 行約 200 バイト」という書き込み形状での値です。1 トランザクションあたりの行数を増やす(バルク INSERT や COPY)と、論理デコードのトランザクション単位のオーバーヘッドが薄まり、同じパイプでも行/秒はもう少し伸びる可能性があります(逆に 1 行ずつコミットすると遅くなりやすいと考えられます)。一方で MB/秒の上限(後述の約 13 MB/秒)はバイト処理側の制約に近いとみられ、行サイズが大きいほど行/秒は下がります。コミット粒度・行サイズを変えた比較は今回は未検証で、今後の課題とします。

ClickPipes の CDC Table Stats でも、cdc_probe への Inserts 約 3,498万 / Updates 約 1,974万が記録され、2 モードの書き込みが想定どおり流れたことが確認できます。

ClickPipes の CDC Table Stats。cdc_probe の Inserts 34,978,499 / Updates 19,740,003(INSERT 負荷と UPDATE 負荷の内訳)

3.2. ClickHouse OLAP はどのレートでも変わらない

OLAP の応答時間(秒)は、負荷なし(P0)と最大負荷(T4、28万行/秒)でほぼ変わりませんでした。

クエリ P0(負荷なし) INSERT T4(28万/秒) UPDATE T4(28万/秒)
Q6(軽) 0.030 0.03〜0.05 0.03〜0.04
Q10(中) 1.34 1.27〜1.42 1.29〜1.44
Q21(重) 2.09 2.31〜2.68 2.09〜2.19

T1〜T3 も同様にフラットでした。一番重い Q21 が INSERT T4 でやや上振れしますが、レートに対して単調ではなく(UPDATE T4 では P0 並みの 2.1 秒台)、CDC 取り込みによる明確な劣化とは言えません。

3.3. ボトルネックは CDC パイプ — スロットのラグが伸び続ける

一方で、レプリケーションスロットのラグ(バイト)はレートに応じてはっきり伸びました。

段階(達成 行/秒) スロットのラグの推移 レプリケーションラグ(秒)
INSERT T2(1万) 約 32 MB で追従(鋸歯) ~10
INSERT T3(5万) 71 → 101 → 132 MB(増え続ける=遅れ始め 4 → 9
INSERT T4(28万) 1.35 → 2.24 → 3.06 GB(急増 13 → 30
UPDATE T4(28万) 1.4 → 2.15 → 2.85 GB(急増 57 → 68

5万行/秒あたりからスロットが溜まり始め、28万行/秒では GB 級まで膨らみます。ClickPipes の CDC Syncs を見ると、10万行のバッチを 1 本ずつ逐次処理 しており、INSERT では 1 バッチ約 3 秒、UPDATE 負荷では 約 5〜6 秒かかっていました。UPDATE の方がバッチ処理が重く、実効的な取り込みレートがさらに落ちます。

ClickPipes の Metrics。10万行のバッチが 1 本ずつ逐次で Completed していく(Pulling は常に 1 本のみ)。Rows synced via CDC のグラフに、INSERT・UPDATE 各段階で取り込みが高止まりする区間が見える

ドレイン中に ClickHouse の行数増分を測ると、15 秒で約 70万行=持続的な取り込みレートはおよそ 4.6万行/秒でした。Managed Postgres のネットワーク送出も約 13 MB/秒で頭打ちになっており、これがちょうど 5万行/秒で遅れ始める閾値と一致します。

3.4. ClickHouse 側はずっと余裕

ClickHouse 側に効かせる狙いだった UPDATE 負荷でも、ClickHouse 側は終始余裕でした。

  • active parts はどのレートでも 4〜10 で、バージョンが増えても急増しない(ClickPipes が効率よくバッチ・マージしている)
  • UPDATE 負荷を最大レートで流した後、cdc_probecount() で 2,098万バージョンを抱えていましたが、FINAL 付きクエリは 0.035 秒で返りました
  • ClickHouse の CPU はピークでも約 1.5 / 3 コア(コンソールの CPU usage per replica)。28万行/秒の取り込み中でも半分しか使っていません

つまり、ミニマルな 3 vCPU の ClickHouse でも、この CDC 経路の取り込みでは計算が飽和しませんでした。

3.5. パイプをチューニングしても上限は動かない

「パイプ自身を速くすれば ClickHouse がボトルネックに変わるのでは」と考え、Pull batch size だけを 10万 → 100万行(10倍) に変えて INSERT の最大負荷を測り直しました(単一変数の A/B、Sync interval は 60 秒のまま)。

指標 batch=100,000 batch=1,000,000
1 バッチの行数 10万 100万
1 バッチの所要 約 3 秒 約 30 秒
取り込みレート 約 4.6万 行/秒 約 3.3〜4.6万 行/秒(ほぼ同じ)
スロットのピーク 約 3.0 GB 約 4.6 GB(悪化)
レプリケーションラグの粒度 数秒刻み 100万行ごとにまとめて反映され、値が大きく上下する(悪化)
ClickHouse CPU(最大) 約 1.5 / 3 コア 約 1.5 / 3 コア(不変)

ドレイン時、ClickHouse の行数はちょうど 100万行ずつ・約 30 秒間隔で増えました。バッチが 10倍になった分、処理時間も約 10倍。取り込みレートは変わりません。バッチを大きくしても ClickHouse CPU は 1.5 コアのままで、ボトルネックに変えることもできませんでした。むしろスロットのピークが増え、レプリケーションラグが粗くなるデメリットだけが出ています。

ClickPipes の CDC Syncs(Pull batch size 100万)。1 バッチ 100万行の処理に 31〜37 秒かかっており、10万行/3 秒のときと 1 行あたりの速度は変わらない

1M バッチ運転中の ClickHouse Infrastructure。CPU usage per replica は依然ピーク約 1.5 / 3 コアで、バッチを大きくしても ClickHouse には余裕がある


4. 考察

4.1. なぜ ClickHouse ではなくパイプが先に頭打ちになるのか

ClickPipes(PeerDB)の CDC は、Postgres のレプリケーションスロットからの論理デコードで WAL を読み出します。論理デコードは 1 スロットあたり実質シングルストリームで、ClickPipes 側も 10万行のバッチを 1 本ずつ逐次に pull → push します(3.3 のコンソール画面で、Pulling 状態のバッチが常に 1 本だけなのが確認できます)。

このため、書き込みレートをいくら上げても、CDC が運べるのは「単一ストリームのバッチ処理スループット」が上限になります。今回の構成では約 3〜5万行/秒(約 13 MB/秒)で、Managed Postgres のネットワーク送出量とも整合しました。ClickHouse 側に届く時点で既に流量が絞られているので、ClickHouse の計算は飽和しようがない、という構図です。

4.2. UPDATE 負荷が INSERT 負荷より重い理由

UPDATE 負荷ではバッチ所要が 3 秒から 5〜6 秒へ延びました。CDC の UPDATE は ReplacingMergeTree へのバージョンの積み増し(実質 upsert)として適用されるため、INSERT より 1 行あたりの処理が重くなります。結果として UPDATE の方が実効的な取り込みレートは低く(約 1.7〜2万行/秒相当)、同じ 28万行/秒を流してもレプリケーションラグがより大きく出ました(68 秒)。ただし parts は 4〜10 のまま、FINAL も 0.035 秒で、ClickHouse 側のマージが追いつかなくなる兆候は見られませんでした。バージョンが溜まっても効率よく解決できていると考えられます。

4.3. パイプのパラメータでは限界を越えられない

Pull batch size を 10倍にしても取り込みレートが変わらなかったことから、この上限は「1 バッチあたりの固定オーバーヘッド」ではなく、デコードと転送・適用そのもののスループットで決まっていると考えられます。ClickPipes の他のパラメータのうち、Parallel threads や Snapshot 系は初期ロード専用で定常 CDC には効かず、Sync interval は backlog がある間はバッチが連続実行されるため上限には影響しません。

4.4. 公式が挙げる継続 CDC のスケール手段(今回は未使用)

ここで補足しておくと、ClickHouse の公式ドキュメントは CDC のスケール手段を分けて説明しています。

  • 初期ロードの並列スナップショット:初期ロード・バックフィルを高速化するための並列化で、定常 CDC のスループットには効きません。今回 Parallel threads / Snapshot 系を変えても定常 CDC が動かなかったことと一致します。
  • 取り込み側コンピュートの増強:ClickPipes の CDC コンピュート(レプリカの CPU/メモリ。スケーリング API で 1〜32 コアまで)を増やす方法と、ClickHouse 側の複数レプリカ+チャンクによる並列取り込みです。

つまり、本記事の約 3〜5万行/秒は CDC コンピュートをデフォルトのまま固定したときの単一ストリーム上限であって、絶対的な限界ではありません。書き込みレートをさらに捌きたい場合の正攻法は、Pull batch size のようなパラメータではなく、この取り込み側コンピュートを増やすことになります(本記事では未検証で、今後の課題とします)。


5. まとめ

観点 結果
ClickHouse OLAP(最大 28万行/秒の取り込み下) P0 比でほぼ不変(重い Q21 で 2.0 → 最大 2.7 秒)
ClickHouse の parts / FINAL parts 4〜10、FINAL 0.035 秒。マージは追いつく
ClickHouse CPU ピーク約 1.5 / 3 コア。常に余裕
CDC パイプの取り込み上限(デフォルトコンピュート時) 約 3〜5万行/秒(約 13 MB/秒)。超過でスロットが GB 級まで増加
Pull batch size 10倍 取り込み上限は不変。スロット・レプリケーションラグはむしろ悪化

「CDC 経由で ClickHouse にどれだけ負荷をかけられるか」という問いの答えは、この経路では ClickHouse の計算にはほとんど負荷をかけられない、でした。ボトルネックは単一ストリームの CDC パイプ側にあり、書き込みを増やしても増えるのは ClickHouse の負荷ではなくレプリケーションラグです。なおこの上限は CDC コンピュートをデフォルトで固定したときの値で、公式が挙げる手段(取り込み側コンピュートの増強)で引き上げられます(本記事では未検証)。

5.1. 今後の課題

  • 書き込み形状を変えた比較:1 トランザクションあたりの行数(バルク INSERT・COPY)や 1 行のサイズを変え、取り込みレート(行/秒・MB/秒)がどう動くか
  • 取り込み側コンピュートの増強:ClickPipes の CDC コンピュート(CPU/メモリ、1〜32 コア)や ClickHouse 側の複数レプリカを増やし、本記事の約 3〜5万行/秒がどこまで上がるか(公式が挙げるスケール手段の実測)
  • 他のマネージド/セルフホスト Postgres での再現:CDC は WAL ベースの汎用的な仕組みのため、同様の傾向が得られると考えられる

参考

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?