コネクションプール

データベース接続を事前に確保してプールし、リクエストごとの接続コストを削減する仕組み

データベースパフォーマンス

コネクションプールとは

コネクションプールは、データベースへの接続 (コネクション) を事前に複数確保しておき、リクエストが来たら空いている接続を貸し出し、処理が終わったらプールに返却する仕組みである。

データベース接続の確立には、TCP ハンドシェイク (3-way handshake)、TLS ネゴシエーション (暗号化接続の場合)、認証処理が必要で、通常 20〜100ms のオーバーヘッドが発生する。リクエストごとに接続を確立・切断すると、このオーバーヘッドが毎回加算される。コネクションプールはこれを初回のみに抑え、以降は確立済みの接続を再利用する。

コネクションプールの動作原理

アプリケーション                    コネクションプール                  データベース
    │                                    │                              │
    ├── 接続要求 ──────────────→ 空き接続を貸出 ─────→ (既存接続を再利用)
    │                                    │                              │
    ├── クエリ実行 ──────────────────────────────────→ 処理実行
    │                                    │                              │
    ├── 接続返却 ──────────────→ プールに戻す                           │
    │                                    │                              │
    └── (接続は切断されない)              └── (次のリクエストで再利用)     │

プールサイズの設計

プールサイズの設定は、パフォーマンスに直結する重要な設計判断だ。

パラメータ 意味 典型的な値
minPoolSize 常時確保する最小接続数 5〜10
maxPoolSize 最大接続数 20〜50
idleTimeout 未使用接続を閉じるまでの時間 30〜300 秒
connectionTimeout 接続取得の待機上限 5〜30 秒

プールサイズの算出式

HikariCP (Java の高性能コネクションプール) の作者は、以下の経験則を提唱している。

最適なプールサイズ ≈ (CPU コア数 × 2) + ディスクスピンドル数

4 コアのサーバーなら、プールサイズ 10 程度が出発点だ。ただし、I/O 待ちが多いワークロードではこれより大きくする必要がある。

プールサイズが大きすぎる場合の問題

直感に反するが、プールサイズを大きくしすぎるとパフォーマンスが低下する。データベース側で大量の接続を維持するメモリコストが増加し、コンテキストスイッチのオーバーヘッドも増える。PostgreSQL では 1 接続あたり約 10MB のメモリを消費する。

Lambda とコネクションプールの問題

Lambda は同時実行数に応じてインスタンスが増えるため、各インスタンスがデータベース接続を確保すると、接続数が爆発的に増加する。

通常のサーバー: 1 プロセス × プールサイズ 20 = 20 接続
Lambda:        1000 同時実行 × 1 接続/インスタンス = 1000 接続

RDS の最大接続数はインスタンスサイズに依存する。

インスタンス メモリ 最大接続数 (概算)
db.t3.micro 1 GiB 約 60
db.t3.medium 4 GiB 約 250
db.r6g.large 16 GiB 約 1,000

Lambda の同時実行数がこれを超えると、too many connections エラーが発生する。

解決策の比較

解決策 仕組み コスト 適するケース
RDS Proxy AWS マネージドの接続プール 月額 $20〜 RDS を使い続ける場合
PgBouncer OSS の接続プーラー (EC2 上) EC2 費用 細かい制御が必要な場合
DynamoDB HTTP API、接続不要 従量課金 サーバーレスネイティブ設計

RDS Proxy

RDS Proxy は Lambda とデータベースの間に入り、コネクションプールを一元管理する。Lambda の同時実行数が 1,000 でも、RDS Proxy がデータベースへの接続を数十本に集約する。

Lambda (1000 同時実行) → RDS Proxy (接続プール) → RDS (50 接続)

IAM 認証にも対応しており、データベースのパスワードを Lambda の環境変数に持たせる必要がなくなる。

DynamoDB なら不要

DynamoDB は HTTP ベースの API でアクセスするため、コネクションプールの問題が根本的に発生しない。サーバーレスアーキテクチャでは DynamoDB を選択する大きな理由の 1 つだ。

よくある落とし穴

  • 接続リーク: 接続を取得した後、例外発生時に返却し忘れるとプールが枯渇する。try-finallyusing パターンで確実に返却する
  • アイドル接続の切断: データベースやネットワーク機器がアイドル接続を切断すると、プール内の接続が無効になる。接続取得時のバリデーション (testOnBorrow) やキープアライブクエリで対策する
  • Lambda のコールドスタートとの相互作用: Lambda のコールドスタート時にコネクションプールが初期化され、接続確立のレイテンシが加算される。Provisioned Concurrency で緩和できる

「High Performance MySQL」(Baron Schwartz ら著) でコネクションプールの設計が詳しく解説されている。

詳しくは関連書籍を参照。

関連用語