判断: コネクションプーリングをどう構成するか?
データベース接続はそれぞれメモリとスケジューリングのコストを伴う。接続数が無秩序に増えるとサーバーが遅くなり、ダウンすることさえある。コネクションプールは、少数の永続的な接続を再利用することでこれを解決する。このツリーは、プールの配置場所、使用するモード (session/transaction/statement)、適切なサイズの判断を導き、サーバーレス環境で外部プーラーが必須である理由を強調する。
アプリケーションアーキテクチャはどのように接続するか?
プーリングの判断はアーキテクチャから始まる。単一の長時間稼働するアプリケーションプロセス (従来型サーバー) か、多数の独立したプロセス/インスタンス (マイクロサービス、多数の Pod) か、それともリクエストごとに生成・消滅するサーバーレス/Lambda 関数か? それぞれで異なるプール配置が必要。
- 1 つ/少数の長時間稼働するアプリケーションプロセス
- 多数のプロセス/インスタンス/Pod が同じ DB に接続する
- サーバーレス / Lambda / 短命な関数
- まず適切なプールサイズの選び方を知りたい
ミドルウェアのプーラーモードはどう選ぶべきか?
クライアントが多い場合、ミドルウェアのプーラー (PgBouncer、ProxySQL) が DB 上の接続数を低く保つ。ただし、プールモードはアプリケーションが使用する機能に依存する: セッションに紐づく状態 (プリペアドステートメント、一時テーブル、SET、アドバイザリロック、セッションレベルの機能) に依存しているか、それともすべてが単一のトランザクション内で完結するか?
- セッション状態に依存している (SET、一時テーブル、セッションのプリペアドステートメント)
- トランザクションは短く独立している。セッション状態は保持しない
推奨: アプリケーション内コネクションプール
1 つ/少数の長時間稼働するプロセスでは、アプリケーション内のプールライブラリ (HikariCP、Go の database/sql の組み込みプール、.NET のコネクションプーリング) でほとんどのニーズを満たせる。接続は一度開かれ、繰り返し再利用される。
推奨: プールを適切なサイズにする
プールは小さすぎても (リクエストが待機する) 大きすぎてもいけない。接続が多すぎると CPU のコンテキストスイッチとロック競合が発生し、スループットが低下する。サイズは際限なく増やす数値ではなく、コア数と I/O に基づく小さな数値。
推奨: セッションプールモード
セッションモードでは、クライアント接続はセッションが終了するまでバックエンドのサーバー接続に固定される。セッションレベルの状態 (SET、一時テーブル、アドバイザリロック、セッションのプリペアドステートメント) は保持されるが、接続の共有は最小限。
推奨: トランザクションプールモード
トランザクションモードでは、サーバー接続はトランザクションの間だけクライアントに貸し出され、トランザクション終了時にプールに戻る。そのため、数千のクライアントが DB 上のごく少数の実接続で動作できる。最も一般的な大規模向けモード。
推奨: サーバーレスには外部プーラーが必須
サーバーレス/Lambda 関数は急速にスケールし、各インスタンスが独自の接続を開く。インスタンスは短命で何も共有しないため、アプリケーション内プールはここでは役に立たない。数百/数千のインスタンスが突然起動すると、DB の接続上限を超えてしまう。解決策は DB の前段に置く外部コネクションプール。