目的:APIの安定性(SLO)を守りつつ、Aurora PostgreSQL・RDS Proxy / PgBouncer の接続数を“攻めすぎず、守りすぎず”最適化する。


0. 前提(SLOを決める)

  • 可用性:99.9%(例)
  • レイテンシ:p95 < 200ms / p99 < 500ms(API Gateway → アプリ → DB往復)
  • エラー率:< 1%

SLOがないと“調整のゴール”が曖昧になる。まず決める。


1. 初期設定(仮説ベース)

  • アプリ側プール上限(per pod)N_app = (目標DB接続総数 ÷ レプリカ数)
  • 目標DB接続総数min( DB最大接続数 × 0.6, CPU・IO の余裕がある範囲 )
  • RDS Proxy / PgBouncermax_client_conn はアプリ総和 + 余裕10–20%
  • PgBouncer の pool_mode:短命クエリ中心なら transaction を基本(prepared statement 常用なら要注意)

例:DB max_connections=400 → 目標 240(60%)→ podが6なら 1podあたり40。


2. 監視メトリクス(最低限)

アプリ(APM / カスタム)

  • p50/p95/p99 レイテンシ(DB区間も計測)
  • スループット(RPS)、エラー率、タイムアウト率
  • プール枯渇回数(borrow wait、timeout 回数)

RDS Proxy / PgBouncer

  • ClientConnections / Active / Waiting(クライアント側の詰まり)
  • DatabaseConnections / Servers in use(DB側に出している本数)
  • Borrow/Acquire Timeouts(借りられずタイムアウト)
  • Pool hit/miss(PgBouncer SHOW STATS / SHOW POOLS

Aurora PostgreSQL(CloudWatch + pg_stat_*)

  • DB CPUUtilization / FreeableMemory
  • DB Connections(実接続)IOPS / Throughput
  • Buffer/Cache hit ratioLock待ち長時間トランザクション
  • pg_stat_activitystate, wait_event, 長時間実行クエリ

3. 手順(負荷をかけて→観察→調整)

  1. ベースライン計測(本番に近い負荷)
    • k6/Locust 等で RPS を段階的に上げる(例:x1 → x2 → x3)。
    • 10〜15分ずつ安定化させ、すべてのメトリクスを記録。
  2. ボトルネック特定
    • アプリ待ち:プール待ち増、p95↑、DBは余裕→ プール不足の疑い。
    • DB過負荷:CPU/IO↑、待ちロック↑、クエリ遅延→ プール出し過ぎの疑い。
    • ネットワーク/他要因:DBもプールも余裕、アプリのみ遅い→別層を調査。
  3. 小さく調整(10–20%刻み)
    • プール上限(アプリ or PgBouncer/RDS Proxy)を +10〜20%
    • 5–10分安定化 → p95/p99・Borrow Timeout・DB CPU/IO を再チェック。
  4. 戻し判断
    • p95は改善したが DB CPU 80%超・IO飽和 → 出し過ぎ。直前の設定に戻す。
    • p95改善なし & Borrow Timeout多発 → アプリ内の同時実行やSQLを見直す。
  5. 収束
    • SLO を満たしつつ、DBリソースに 20–30% の余力が残るポイントで固定。

4. クイック診断のクエリ/コマンド

-- 過剰な長時間トランザクション
SELECT pid, usename, state, wait_event, xact_start, now()-xact_start AS xact_age, query
FROM pg_stat_activity
WHERE state <> 'idle' AND xact_start IS NOT NULL
ORDER BY xact_start;

-- ロック待ち
SELECT bl.pid AS blocked_pid, ka.query AS blocked_query, kl.pid AS locking_pid, la.query AS locking_query, bl.mode, bl.granted
FROM pg_locks bl
JOIN pg_stat_activity ka ON ka.pid = bl.pid
JOIN pg_locks kl ON bl.locktype = kl.locktype AND bl.DATABASE = kl.DATABASE AND bl.relation = kl.relation AND bl.page = kl.page AND bl.tuple = kl.tuple AND bl.classid = kl.classid AND bl.objid = kl.objid AND bl.objsubid = kl.objsubid AND bl.mode = kl.mode AND kl.granted
JOIN pg_stat_activity la ON la.pid = kl.pid
WHERE NOT bl.granted;
# PgBouncer
psql -p 6432 -U pgbouncer -h <proxy> pgbouncer -c "SHOW STATS;"
psql -p 6432 -U pgbouncer -h <proxy> pgbouncer -c "SHOW POOLS;"

5. よくある症状 → 先に効く対処

  • Borrow/Acquire Timeoutが出る
    • アプリのプール上限↑(少しずつ)/SQL時間短縮/N+1排除/インデックス最適化
  • DB CPU/IO が高止まり
    • プール上限↓/遅いクエリの改善/読み取りはリードレプリカへ/キャッシュ導入
  • フェイルオーバーでスパイク
    • RDS Proxy の再接続/コネクション TTLを短めに/アプリ側の再試行(指数バックオフ)
  • PgBouncer + prepared statements 問題
    • transaction モードで server_reset_query 設定、もしくは session モードに切替(スループットとトレードオフ)

6. 目安式(ラフ設計)

  • リトルの法則同時DB接続 ≒ RPS × 平均DB処理時間(秒)
    • 例:RPS 300 × 0.05s ≒ 15 → 安全率×2〜3 → 30〜45 を入口の目安に。
  • max_connections:PostgreSQL のメモリ事情を鑑み 実効60–70%以内に抑える。

7. ガードレール

  • 回路遮断(Circuit Breaker):エラー多発時は早めに弾く
  • 優先度キュー:オンライン推論を最優先、バッチは後回し
  • 動的制限:DB負荷に応じてアプリ同時実行数を制御
  • コネクションTTL / 最大アイドル時間:ゾンビ接続を減らす

まとめ

“小さく上げて、全層を見て、戻す勇気”。この反復が最短距離。SLOに照らし、DBに20–30%の余力を残したところが“ちょうどいいプール”です。

投稿者 kojiro777

“接続プールの“見ながら調整”実践ガイド” に1件のフィードバックがあります

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です