Aurora PostgreSQL深夜2時の接続切れで学んだ、本番安定化の3つの現実
本番環境で3度のDB障害を経験。接続プール枯渇・バキューム遅延・デフォルト設定の罠を乗り越えた3年の実装知見を、実際のトラブルシューティングコードと共に公開します。
Aurora PostgreSQL深夜障害で学んだ、2026年版RDS運用の本当のコツ
深夜2時。Slackの通知音で目が覚めた。いつものやつだ——「Database connection limit exceeded」。
うちのチームがAurora PostgreSQLで本番を止めたのはこれで3度目。前回は接続プール枯渇、その前はバキューム遅延。毎回同じパターンで怒られて、「デフォルト設定を信じればなんとかなる」という幻想が完全に吹き飛んだ。
正直、PostgreSQLの設定ドキュメントは地獄だ。パラメータは数百個あるし、本番環境での実測値と推奨値が全然違う。でも3年間の試行錯誤を通じて、「実際に本番で動く設定」がようやく見えてきた。
RDS運用で最初に学ぶべき「接続管理」の現実
最初の失敗は単純だった。接続数の上限を計算してなかったんだ。
デフォルトだとmax_connectionsは自動で設定されるんだけど、うちは急成長してアプリケーションサーバーを8台に増やした。各サーバーから10個の接続が来るから、80個の接続。余裕があるなって思ってた。
でも実際には、こんなふうに確認してみたら:
-- 接続の内訳を確認
SELECT datname, usename, state, count(*)
FROM pg_stat_activity
GROUP BY datname, usename, state;
アイドル接続が150個以上残ってた。キープアライブがOFFになってたせいで、閉じられずに溜まってたんだ。
接続プール(PgBouncer)の導入が救いだった。正直、2026年時点で本番PostgreSQLを直接つなぐのはもう時代遅れだと言い切れる。
# pgbouncer.ini の基本設定
[databases]
mydb = host=aurora-endpoint.rds.amazonaws.com port=5432 user=postgres password=xxxxx
[pgbouncer]
pool_mode = transaction # 重要: トランザクション終了後に接続を返す
max_client_conn = 1000
default_pool_size = 25 # 実験的に設定、監視必須
reserve_pool_size = 5
reserve_pool_timeout = 3
transactionモードが大事。session モードだと接続を再利用できず、コネクション枯渇のリスクが高まる。うちはデフォルトの session モードで3ヶ月ハマった。
本番環境でのコネクション数は、実際に負荷テストをやらないと分からない。ドキュメントに書いてある「ベストプラクティス」は、たいていあなたの環境には合ってないんだ。
バキューム地獄に5ヶ月ハマった話
接続プール問題を解決してから2ヶ月後、今度は「テーブルがデッドロック状態」という別の悪夢が襲ってきた。
updateやdeleteで大量のデータを扱うテーブルがあるんだけど、バキューム処理が追いつかなくなってたんだ。結果、テーブルが膨張し続けて、クエリのスキャンコストが爆上がり。状況を把握するために、こんなクエリを走らせてみたら:
-- テーブルサイズを確認
SELECT
schemaname,
tablename,
pg_size_pretty(pg_total_relation_size(schemaname||'.'||tablename)) AS size,
ROUND(100 * (pg_total_relation_size(schemaname||'.'||tablename) - pg_relation_size(schemaname||'.'||tablename)) / pg_total_relation_size(schemaname||'.'||tablename)) AS index_ratio
FROM pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
ORDER BY pg_total_relation_size(schemaname||'.'||tablename) DESC;
ある1つのテーブルが450GBに……。本来なら150GBで済むはずだったんだ。デッドタプルが溜まりまくってた。
デフォルトのオートバキューム設定じゃ、全然追いつかないんだよ。試行錯誤の末、本番で動いた設定がこれ:
-- 大型テーブル向けバキューム設定
ALTER TABLE large_table SET (
autovacuum_vacuum_scale_factor = 0.01, -- デフォルト0.2を10分の1に
autovacuum_vacuum_cost_delay = 2, -- ディスクI/O負荷を下げる
autovacuum_vacuum_cost_limit = 200 -- ディスク負荷と実行頻度のバランス調整
);
でも正直、これでも完璧じゃない。実装してから1週間後、深夜にまたバキューム遅延でアラートが。パラメータをさらに調整したり、不要なインデックスを削除したり、試行錯誤の連続だった。
2026年の結論としては、「バキューム」は設定だけじゃなく、スキーマ設計の段階から意識しないと地獄を見るってことだ。チームメンバーも「DELETE/UPDATEしまくる方法は避けよう」という文化を作るのに3ヶ月かかったんだからね。
CloudWatch メトリクス監視で本番を守る実装
3度目の大障害を経験してから、チーム全体で監視を本気で整備することにした。こんなスクリプトで定期的にメトリクスを取得してる:
#!/bin/bash
# CloudWatch メトリクス取得スクリプト
aws cloudwatch get-metric-statistics \
--namespace AWS/RDS \
--metric-name DatabaseConnections \
--dimensions Name=DBInstanceIdentifier,Value=aurora-prod \
--start-time 2026-08-09T00:00:00Z \
--end-time 2026-08-10T00:00:00Z \
--period 300 \
--statistics Average,Maximum
これを定期的に見てると、パターンが見えてくる。うちの環境では、こんなふうに動いてる:
| 時間帯 | 接続数 | 理由 |
|---|---|---|
| 朝8時 | 100→150 | ユーザーログイン時間 |
| 昼12時 | 150(安定) | ピーク時間帯 |
| 夜12時 | 150→30 | 夜間バッチ完了 |
| 深夜2-4時 | 30→120(スパイク) | データウェアハウス同期 |
このパターンを理解してから、バッチ処理のタイミングを変えたり、接続プールサイズを動的に調整したりできるようになった。
AWS RDS Enhanced Monitoring も導入した。これでPostgreSQLプロセスレベルの詳細が見えるんだ:
{
"cpuUtilization": {
"guest": 0,
"irq": 0.1,
"user": 25.3,
"system": 8.2,
"wait": 12.1
},
"diskIO": [
{
"readLatency": 2.1,
"writeLatency": 4.3,
"readThroughput": 50.2,
"writeThroughput": 120.5
}
]
}
この data から、「I/O待機が多い時間帯は接続数を制限しよう」とか「バッチ処理の順序を変えよう」とかいう判断ができる。これが本当に役立つんだ。
2026年版・本番PostgreSQL構成図
graph TB
subgraph "Application Layer"
App1["App Server 1"]
App2["App Server 2"]
App3["App Server 3"]
end
subgraph "Connection Pool Layer"
PGB1["PgBouncer (transaction mode)"]
PGB2["PgBouncer (Replica)"]
end
subgraph "AWS RDS"
subgraph "Primary"
AuroraPrimary["Aurora PostgreSQL 17<br/>instance: db.r7g.2xlarge"]
end
subgraph "Read Replicas"
AuroraRead1["Read Replica 1<br/>db.r7g.xlarge"]
AuroraRead2["Read Replica 2<br/>db.r7g.xlarge"]
end
end
subgraph "Monitoring & Backup"
CloudWatch["CloudWatch<br/>Enhanced Monitoring"]
EventBridge["EventBridge<br/>Alert Rules"]
BackupVault["Automated Backup<br/>35-day retention"]
end
App1 -->|Connection| PGB1
App2 -->|Connection| PGB1
App3 -->|Connection| PGB1
PGB1 -->|Pooled Connections| AuroraPrimary
PGB2 -->|Read Pool| AuroraRead1
PGB2 -->|Read Pool| AuroraRead2
AuroraPrimary -->|Replication| AuroraRead1
AuroraPrimary -->|Replication| AuroraRead2
AuroraPrimary -->|Metrics| CloudWatch
CloudWatch -->|Alert| EventBridge
AuroraPrimary -->|Backup| BackupVault
見ての通り、もはやPostgreSQLをダイレクトにつなぐことはしない。すべてコネクションプールを経由する。そして監視は複層。これでようやく「深夜2時の電話」が減ったんだ。
パラメータチューニング:実測で決めるの重要性
postgresql.conf の重要パラメータは、RDS のパラメータグループで管理する。2026年時点で、うちが本番で使ってる設定がこれ:
shared_buffers = 16GB # インスタンス RAM の 25%
effective_cache_size = 48GB # RAM の 75%
work_mem = 256MB # shared_buffers / max_connections
maintenance_work_mem = 4GB
wal_buffers = 16MB
default_statistics_target = 100 # クエリプランニング精度向上
random_page_cost = 1.1 # SSD での推奨値
effective_io_concurrency = 200 # SSD での推奨値(NVMe対応)
でも正直なところ、これらの値は「テンプレート」に過ぎない。本当には、アプリケーションのワークロードを見て調整する必要がある。
実装直後1ヶ月は、毎日 CloudWatch を見て、パラメータを微調整した。特に work_mem は大きすぎるとメモリリークを招くし、小さすぎるとディスク I/O が増える。適切な値は、本番データ量と同時実行数で決まるんだ。
バージョンアップデートのリスク
2026年は PostgreSQL 17 が安定版になった。うちは 16 から 17 への移行を検討してるんだけど、本番環境での検証に3ヶ月予定してる。
理由は単純——バージョンアップで挙動が変わることがあるからだ。過去に 14→15 アップグレードで、特定のクエリが 10倍遅くなったことがある。プランナーの改善のせいだったんだけど、統計情報を再取得したら戻ったんだ。
-- メジャーアップグレード前の検証
ANALYZE; -- 統計情報を再計算
VACUUM; -- デッドタプル削除
-- 本番相当のクエリでプラン比較
EXPLAIN ANALYZE
SELECT * FROM large_table WHERE condition;
本番環境でのアップグレードは、レプリカを経由する Blue-Green デプロイ方式で実施する。ダウンタイムなしで新バージョンを検証できるから、リスクが大幅に下がるんだ。
本当に必要な3つの運用ルール
個人的には、これら3つが揃ってないと本番は安定しないと強く感じてる:
1. 接続プール導入は必須(PgBouncer or RDS Proxy)
- デフォルトの session モードは使わない
- 環境に応じて transaction モードのパラメータを調整する
2. バキューム設定は static じゃなく dynamic(テーブルごとに調整)
- 自動バキュームはあくまで補助手段
- テーブルの書き込みパターンに応じて細かく調整が必要
3. 監視は “数値” で、感覚では判断しない(CloudWatch Enhanced Monitoring 必須)
- パターン認識が本番安定性を左右する
- 異常値を検出したら即座にアラートを設定
これらがないと、深夜の電話は避けられない。
まとめ
3年間の運用経験から言えることは、PostgreSQL本番運用は「地味な作業」の連続ってこと。派手な新機能より、日々の監視と小さな調整の積み重ねが本当に大事なんだ。
- 接続管理: PgBouncer の transaction モードで接続枯渇を防止。デフォルトの session モードは本番向きじゃない
- バキューム: 自動バキューム設定は表面的な解決に過ぎない。テーブルごとに細かく調整し、スキーマ設計段階から意識が必要
- 監視: CloudWatch Enhanced Monitoring で I/O・CPU・メモリの詳細把握。パターン認識が本番安定性を左右する
- パラメータ: テンプレート値は参考に過ぎず、本番ワークロードで実測値に基づく調整が必須
- アップグレード: Blue-Green デプロイで検証期間を確保。本番での予期しない挙動を事前に発見できる
2026年時点で、PostgreSQL 本番運用はツール + ナレッジ + 監視の組み合わせなんだ。どれが欠けても本番は不安定になる。深夜 2 時の電話は避けたいならね。