2026年10月12日
2026年10月12日
PostgreSQLのmax_connections最適化設定:上げる前に確認することと接続プーリング
PostgreSQL で FATAL: sorry, too many clients already が出たとき、max_connections を大きくすれば一時的には解消します。しかし、PostgreSQL は1接続ごとに1つのプロセスを立ち上げる設計なので、むやみに増やすとメモリ不足や性能低下を招きます。この記事では、現在の接続の使われ方を調べる方法、max_connections の決め方と変更手順、そして多くの場合で根本的な対処になる接続プーリングとタイムアウト設定を説明します。
なお、WordPress のコアが正式に対応しているデータベースは MySQL と MariaDB で、PostgreSQL には対応していません(2026年10月現在)。この記事は、PostgreSQL を使う別のアプリケーションや API サーバーを運用している場合を想定しています。
max_connectionsとは
max_connections は、同時に張れる接続数の上限です。既定値は 100 で、パッケージや初期化ツールによっては環境に合わせた値が設定されることもあります。この上限のうち一部は管理者用に予約されています。
- superuser_reserved_connections: スーパーユーザー用に残しておく枠(既定は 3)。接続が埋まっても管理者が入って調査できるようにするためのものです。
- reserved_connections: PostgreSQL 16 で追加された、
pg_use_reserved_connectionsロールを持つロール向けの予約枠です。
一般ユーザーの接続が上限に達すると too many clients already、予約枠だけが残っている状態では remaining connection slots are reserved for ... というエラーになります。
まず現状を調べる
上限を変える前に、接続が何に使われているかを確認します。上限に達する原因の多くは、処理中ではなく「つなぎっぱなし」の接続です。
SHOW max_connections;
SELECT state, count(*)
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY state
ORDER BY count(*) DESC;
結果の state の読み方は次のとおりです。
- active: クエリを実行中。CPU やディスクを実際に使っている接続です。
- idle: 接続しているだけで何もしていない。アプリ側のコネクションプールや持続的接続が保持しているものです。
- idle in transaction: トランザクションを開いたまま止まっている。ロックを保持し続け、VACUUM の妨げにもなるため、多い場合はアプリ側の不具合を疑います。
接続元ごとの内訳を見ると、どのアプリケーションやサーバーが多くの接続を使っているかがわかります。
SELECT usename, application_name, client_addr, state, count(*)
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY usename, application_name, client_addr, state
ORDER BY count(*) DESC
LIMIT 20;
idle が大半を占めているなら、上限を上げるより、アプリ側の接続の持ち方を見直すほうが効果的です。
よくある原因
アプリケーションサーバーの台数×プロセス数
PHP-FPM のワーカーそれぞれが持続的接続(pg_pconnect など)を持つ構成では、「サーバー台数 × ワーカー数」がそのまま接続数になります。オートスケールで台数が増えると、アクセスが少なくても接続が上限に届きます。アプリ側のコネクションプールでも、プールの最大サイズ × インスタンス数を合計して上限と比べてください。
閉じられない接続・長時間のトランザクション
エラー時に接続を閉じない、トランザクションを開いたまま外部 API の応答を待つ、といった実装は idle in transaction を増やします。
重いクエリによる滞留
1件あたりの処理が遅くなると、同時に実行中の接続が積み上がります。この場合は接続数ではなく、遅いクエリ自体を改善するのが先です。
max_connectionsの決め方
適切な値は、ハードウェアとワークロードで変わるため、決まった公式はありません。目安として次の2点を考えます。
- 実際に同時に処理できる数: 同時に active な接続が CPU コア数を大きく超えても、処理は速くなりません。待ち行列がサーバー内部に移るだけで、切り替えのコストやロックの競合が増えます。
- メモリ: 接続ごとにプロセス自体のメモリが必要です。加えて、ソートやハッシュ処理ごとに最大
work_memまで使うため、1つのクエリがwork_memの数倍を使うこともあります。shared_buffersに加え、「接続数 × 1接続あたりの使用量」がメモリに収まるかを見積もります。
max_connections を上げるなら、work_mem を大きく設定していないかも同時に確認してください。両方を大きくすると、負荷が集中したときにメモリ不足(OOM Killer によるプロセスの強制終了)を起こす危険があります。
変更手順
max_connections はサーバーの再起動が必要な設定です。pg_reload_conf() では反映されません。メンテナンス時間を確保し、事前に設定ファイルのバックアップを取ってから作業します。
ALTER SYSTEM SET max_connections = 200;
ALTER SYSTEM は、データディレクトリの postgresql.auto.conf に設定を書き込みます。postgresql.conf を直接編集する方法もありますが、両方に書くと postgresql.auto.conf の値が優先されるので、どちらで管理するかを決めておきます。
sudo systemctl restart postgresql
サービス名はディストリビューションやバージョンによって異なります。再起動後に SHOW max_connections; で値を確認してください。レプリケーション構成では、スタンバイ側の max_connections をプライマリ以上にしておく必要がある点にも注意します。
ロールやデータベースごとに上限を設ける
特定のアプリケーションが接続を使い切ると、他のアプリケーションまで巻き込まれます。ロールやデータベース単位で上限を設けると、影響範囲を限定できます。
ALTER ROLE app_rw CONNECTION LIMIT 80;
ALTER DATABASE appdb CONNECTION LIMIT 120;
タイムアウトで放置された接続を減らす
放置された接続を自動で切る設定も有効です。値は例なので、アプリケーションの処理時間に合わせて調整してください。
ALTER ROLE app_rw SET idle_in_transaction_session_timeout = '60s';
ALTER ROLE app_rw SET statement_timeout = '30s';
PostgreSQL 14 以降には、何もしていない接続を切る idle_session_timeout もあります。ただし、アプリ側のコネクションプールが保持している接続まで切られるため、プール側の再接続処理が整っていることを確認してから使ってください。
接続プーリングを導入する
接続数の問題に対して多くの場合で効果が大きいのが、PgBouncer などの接続プーラーを間に置く方法です。アプリケーションからは多数の接続を受け付けつつ、PostgreSQL へは少数の接続を使い回します。
PgBouncer には主に次のモードがあります。
- session モード: クライアントが切断するまで1本のサーバー接続を占有します。互換性は高いものの、接続数を減らす効果は限られます。
- transaction モード: トランザクションが終わるたびにサーバー接続を返却します。接続数を大きく減らせる一方、セッション単位の機能(
SETで変えた設定、一時テーブル、アドバイザリーロックなど)は期待どおりに動かないことがあります。プリペアドステートメントの扱いは PgBouncer のバージョンで改善が進んでいるため、利用するバージョンのドキュメントで確認してください。
導入するときは、ステージング環境でアプリケーションの全機能を通して試し、特に transaction モードではセッション依存の処理がないかを確認します。
マネージドサービスの場合
Amazon RDS for PostgreSQL や Aurora PostgreSQL では、max_connections の既定値がインスタンスのメモリ量から計算され、パラメータグループで変更します。変更に再起動が必要かどうかや計算式は、サービスとバージョンによって異なるため、公式ドキュメントで確認してください。接続のプーリングには RDS Proxy を使う選択肢もありますが、対応するエンジンのバージョンや制約があるので、事前に条件を確かめておきます。
まとめ
too many clients alreadyが出たら、上限を上げる前にpg_stat_activityで接続の状態と接続元を調べる。idleやidle in transactionが多いなら、アプリ側の接続の持ち方とトランザクションの書き方を見直す。max_connectionsは CPU で同時に処理できる数とメモリ(work_memを含む)から決め、変更には再起動が必要。- ロールごとの
CONNECTION LIMITやタイムアウトで、1つのアプリが接続を使い切るのを防ぐ。 - 根本的な対処には PgBouncer などの接続プーラーが有効。transaction モードの制約はステージングで確認する。