SO_REUSEPORT とピアリングを使用した PgBouncer スループット 4 倍化
SO_REUSEPORT とピアリングを使用した PgBouncer スループット 4 倍化
ClickHouse が PgBouncer のスループットを 4 倍に向上させた
ClickHouse は、単一マシン上で複数の PgBouncer プロセスを実行することで、PgBouncer のスループットを 4 倍にスケールさせました。このアプローチは、通常は単一 CPU コアに性能が制限される PgBouncer のシングルスレッド特性を克服し、SO_REUSEPORT ソケットオプションを使用して接続負荷を複数プロセスに分散させます。
負荷分散に SO_REUSEPORT を使用する
高スループットを実現するために、ClickHouse は SO_REUSEPORT を活用しています。このソケットオプションにより、複数のプロセスが同じ IP アドレスとポートにバインドできるようになります。Linux カーネルは、ラウンドロビンまたはハッシュ方式で受信した TCP 接続をプロセス間に分配します。これにより、単一の PgBouncer インスタンスがマルチプロセスアーキテクチャに変換され、プロセスプールに割り当てられた CPU コア数に比例してシステムが線形にスケールします。
ピアリングでクエリキャンセル問題を解決する
SO_REUSEPORT が接続を分散させる一方で、課題が生じます。クエリキャンセル要求が、セッションを所有していない PgBouncer プロセスに届く可能性があるのです。標準的な PgBouncer アーキテクチャは孤立しているため、セッションを認識しないプロセスがキャンセル要求を受け取っても通常は無視され、クエリキャンセルが失敗します。
この問題を解決するために、ClickHouse はピアリング機構を実装しました。ピアリングにより PgBouncer プロセス同士が相互に通信できるようになります。プロセスが所有していないセッションのキャンセル要求を受け取った場合、実際にそのセッションを管理しているピアプロセスへ要求を転送します。これにより、分散したプロセスプールでもクエリキャンセルが正しく機能します。
実装と設定
コミュニティでの議論に基づき、この構成を有効にするには so_reuseport をオンにし、ピア ID と対応するソケットを定義する必要があります。設定例は以下の通りです。
[pgbouncer]
listen_addr = 0.0.0.0
listen_port = 6432
so_reuseport = 1
peer_id = 1
unix_socket_dir = /tmp/pgbouncer1
[peers]
1 = host=/tmp/pgbouncer1
2 = host=/tmp/pgbouncer2
3 = host=/tmp/pgbouncer3
4 = host=/tmp/pgbouncer4
コミュニティの洞察と代替案
この実装に関する技術的議論では、PostgreSQL 接続プーリングをスケールさせるためのいくつかの代替案や考慮点が指摘されています。
- 代替プーラー: 一部のユーザーは、PgBouncer のスケーラブルな代替として Odyssey や pgdog の使用を提案しています。
- インフラレベルのスケーリング: Kubernetes 上で複数の PgBouncer インスタンスを実行することは、複数マシンにまたがって同様の結果を得るシンプルな方法として挙げられ、クラウドプロバイダーの VM メンテナンスに伴うリスク軽減に役立ちます。
- アーキテクチャ上の質問: 一部のエンジニアは、複数の PgBouncer インスタンスへトラフィックを分散させる従来の HAProxy 設定と比べた際のこのアプローチの利点について疑問を呈しています。
"キャンセルはクエリを全く知らないプロセスに届き、何も起きません。ピアリングがこれを解決します。プロセス同士が互いを認識しているため、誤ったプロセスに届いたキャンセルは実際にセッションを所有しているプロセスへ転送されます。"
このピアリング機構は、マルチプロセス PgBouncer 環境において PostgreSQL の接続管理機能を維持するために重要です。