PostgreSQLの現在の接続数は、SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'client backend'; で確認でき、上限は SHOW max_connections; で確認できます。 pg_stat_activity は「いま接続しているセッションを1行ずつ見せてくれるビュー」で、誰が・どのデータベースに・どんな状態でつながっているかまで分かります。

この記事では、確認 → 原因の特定 → 切断 → 上限の変更 → 再発防止の流れを1本にまとめました。「FATAL: sorry, too many clients already」を実際に再現し、集計 SQL・切断・設定変更の出力をすべて PostgreSQL 16.15 で実行した結果として載せています(検証用に max_connections を 20 にした専用のサーバーを使用。パスなどの表示は環境に合わせて置き換えています)。

PostgreSQL接続数の確認と対処の早見表

やりたいこと SQL・コマンド 補足
上限を確認する SHOW max_connections; 既定は多くの環境で 100
現在の接続数を確認する SELECT count(*) FROM pg_stat_activity WHERE backend_type = 'client backend'; 条件なしだと内部プロセスも数に入る
state 別に数える SELECT state, count(*) FROM pg_stat_activity WHERE backend_type = 'client backend' GROUP BY state; idle が多いか、idle in transaction が多いかを見る
DB 別に数える SELECT datname, count(*) FROM pg_stat_activity WHERE backend_type = 'client backend' GROUP BY datname; pg_stat_database.numbackends でも可
ユーザー別に数える SELECT usename, count(*) FROM pg_stat_activity WHERE backend_type = 'client backend' GROUP BY usename; 接続元アプリの見当を付ける
接続元 IP 別に数える SELECT client_addr, count(*) FROM pg_stat_activity WHERE backend_type = 'client backend' GROUP BY client_addr; NULL は Unix ソケット接続
実行中の SQL だけ止める SELECT pg_cancel_backend(pid); 接続は残る
接続ごと切断する SELECT pg_terminate_backend(pid); トランザクションはロールバックされる
放置トランザクションを自動で切る SET idle_in_transaction_session_timeout = '5min'; ロール単位・DB 単位でも設定できる
上限を変える ALTER SYSTEM SET max_connections = 50; → 再起動 リロードでは反映されない
ロール単位で上限を付ける ALTER ROLE batch_user CONNECTION LIMIT 1; 再起動は不要

エラーメッセージと意味(今回すべて再現)

メッセージ 起きる条件 まず行うこと
FATAL: sorry, too many clients already 接続数が max_connections に達した スーパーユーザーも入れない。既存のセッションから切断する
FATAL: remaining connection slots are reserved for roles with the SUPERUSER attribute 一般ロール用の枠を使い切った(残りはスーパーユーザー用の予約枠) スーパーユーザーで接続して状況を確認する
FATAL: remaining connection slots are reserved for roles with privileges of the "pg_use_reserved_connections" role 16以降の reserved_connections の枠だけが残っている 予約枠を使えるロールかスーパーユーザーで接続する
FATAL: too many connections for role "batch_user" ロールの CONNECTION LIMIT に達した そのロールの接続を減らすか上限を見直す
FATAL: too many connections for database "t254" データベースの CONNECTION LIMIT に達した そのデータベースへの接続を減らすか上限を見直す

全体の流れ

flowchart TD
  A[接続数と上限を確認] --> B[state別・DB別・ユーザー別・IP別に集計]
  B --> C{多いのはどの状態か}
  C -->|idle が多い| D[プール設定の見直し・idle_session_timeout]
  C -->|idle in transaction が多い| E[コミット漏れの調査・タイムアウト設定]
  C -->|active が多い| F[時間のかかるSQLの調査]
  D --> G[急ぐときは pg_terminate_backend で切断]
  E --> G
  F --> G
  G --> H[上限の変更またはプーラーの導入を検討]

接続数と上限を確認する

上限に関わる設定を一度に見る

SELECT name, setting, context
FROM pg_settings
WHERE name IN ('max_connections','superuser_reserved_connections','reserved_connections',
               'idle_in_transaction_session_timeout','idle_session_timeout')
ORDER BY 1;

実行結果(PostgreSQL 16.15。検証用に max_connections = 20 としたサーバー):

                name                 | setting |  context   
-------------------------------------+---------+------------
 idle_in_transaction_session_timeout | 0       | user
 idle_session_timeout                | 0       | user
 max_connections                     | 20      | postmaster
 reserved_connections                | 0       | postmaster
 superuser_reserved_connections      | 3       | postmaster
(5 rows)

context が postmaster の設定は、変更に再起動が必要です。 user の設定はセッション単位で変えられます。

現在の接続数を数える

検証のために、アプリ用ロール app_user、バッチ用 batch_user、集計用 report_user で合計8本の接続を張った状態で確認します。

SELECT count(*) FROM pg_stat_activity;
SELECT backend_type, count(*) FROM pg_stat_activity GROUP BY 1 ORDER BY 2 DESC, 1;
 count 
-------
    14
(1 row)

         backend_type         | count 
------------------------------+-------
 client backend               |     9
 autovacuum launcher          |     1
 background writer            |     1
 checkpointer                 |     1
 logical replication launcher |     1
 walwriter                    |     1
(6 rows)

条件を付けずに数えると、内部のプロセス5つも数に入って 14 になります。 クライアントからの接続は backend_type = 'client backend' の 9 本です(張った8本+この確認を実行している自分自身)。max_connections と比べるときは、こちらの数字を使います。

残りの接続数を計算する

SELECT current_setting('max_connections')::int                AS max_conn,
       current_setting('superuser_reserved_connections')::int AS su_reserved,
       current_setting('reserved_connections')::int           AS reserved,
       count(*)                                               AS used,
       current_setting('max_connections')::int - count(*)     AS free_total,
       current_setting('max_connections')::int
         - current_setting('superuser_reserved_connections')::int
         - current_setting('reserved_connections')::int
         - count(*)                                           AS free_for_normal
FROM pg_stat_activity
WHERE backend_type = 'client backend';
 max_conn | su_reserved | reserved | used | free_total | free_for_normal 
----------+-------------+----------+------+------------+-----------------
       20 |           3 |        0 |    9 |         11 |               8
(1 row)

一般のロールが使えるのは「max_connections − superuser_reserved_connections − reserved_connections」までです。 上限が 20 でも、アプリが使える枠は 17 本という計算になります。

pg_stat_activityの見方(主要な列とstateの意味)

主要な列の一覧

列 意味 見るポイント
pid サーバー側プロセスの ID 切断・キャンセルの対象を指定するのに使う
datname 接続先のデータベース名 DB 別の偏りを見る
usename 接続しているロール名 どのアプリの接続かの手がかり
application_name クライアントが名乗っている名前 接続文字列で設定しておくと特定が楽になる
client_addr / client_port 接続元の IP アドレスとポート NULL は Unix ソケット経由
backend_start 接続した時刻 長く残っている接続を探す
xact_start 現在のトランザクションの開始時刻 トランザクション中でなければ NULL
query_start 実行中(または最後)の SQL の開始時刻 実行時間の計算に使う
state_change state が最後に変わった時刻 「その状態が何分続いているか」を計算する
state セッションの状態(次の表) 対処を決める一番の手がかり
wait_event_type / wait_event 待っている対象 Client / ClientRead は「クライアントの次の命令を待っている」
query 実行中の SQL。active 以外では最後に実行した SQL idle の行の query は「いま動いている SQL」ではない
backend_type プロセスの種類 クライアント接続は client backend

stateの値と意味

state 意味 多いときに考えること
active SQL を実行中 時間のかかる SQL やロック待ちが無いか
idle 次の命令を待っている(トランザクションの外) コネクションプールの保持数が多すぎないか
idle in transaction トランザクションを開いたまま、何も実行していない COMMIT / ROLLBACK を忘れた処理が無いか
idle in transaction (aborted) トランザクション内でエラーが起きたまま、終了されていない エラー後の ROLLBACK が抜けていないか
fastpath function call fast-path 関数を実行中 目にする機会は少ない
disabled track_activities が無効で状態を取得していない 設定を確認する

列と state の説明は PostgreSQL 16 の公式文書(The Cumulative Statistics System のページ)で確認しました。今回の検証で実際に観測したのは active・idle・idle in transaction・idle in transaction (aborted) の4つです。

1セッション分の全列を見る

\x
SELECT * FROM pg_stat_activity WHERE state = 'idle in transaction' LIMIT 1;
-[ RECORD 1 ]----+-------------------------------
datid            | 16407
datname          | shop
pid              | 4434
leader_pid       | 
usesysid         | 16404
usename          | app_user
application_name | psql
client_addr      | 127.0.0.1
client_hostname  | 
client_port      | 40002
backend_start    | 2026-10-03 17:34:56.700908+09
xact_start       | 2026-10-03 17:34:56.786909+09
query_start      | 2026-10-03 17:34:56.786957+09
state_change     | 2026-10-03 17:34:56.78762+09
wait_event_type  | Client
wait_event       | ClientRead
state            | idle in transaction
backend_xid      | 
backend_xmin     | 
query_id         | 
query            | select count(*) from pg_class;
backend_type     | client backend

query に SQL が表示されていても、state が idle in transaction なら、その SQL はもう終わっています。 いまは「トランザクションを開いたまま、クライアントの次の命令を待っている」状態です。

接続の内訳を集計するSQL(実行結果つき)

state別

SELECT state, count(*)
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY state ORDER BY count(*) DESC;
             state             | count 
-------------------------------+-------
 idle                          |     4
 active                        |     2
 idle in transaction           |     2
 idle in transaction (aborted) |     1
(4 rows)

DB別

SELECT datname, count(*)
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY datname ORDER BY 2 DESC;
 datname  | count 
----------+-------
 shop     |     7
 postgres |     1
 t254     |     1
(3 rows)

同じ数字は SELECT datname, numbackends FROM pg_stat_database; でも得られました(shop 7、postgres 1、t254 1)。

ユーザー別

SELECT usename, count(*)
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY usename ORDER BY 2 DESC;
   usename   | count 
-------------+-------
 app_user    |     6
 batch_user  |     1
 postgres    |     1
 report_user |     1
(4 rows)

クライアントIP別

SELECT coalesce(host(client_addr), '(unix socket)') AS client, count(*)
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY 1 ORDER BY 2 DESC;
    client     | count 
---------------+-------
 127.0.0.1     |     7
 (unix socket) |     2
(2 rows)

今回は1台の中で検証したため、TCP 接続はすべて 127.0.0.1 です。アプリケーションサーバーが複数ある環境では、ここにサーバーごとの IP が並び、どのサーバーが接続を多く持っているかが分かります。

DB×ユーザー×stateの組み合わせ

SELECT datname, usename, state, count(*)
FROM pg_stat_activity
WHERE backend_type = 'client backend'
GROUP BY 1,2,3 ORDER BY 4 DESC, 1,2,3;
 datname  |   usename   |             state             | count 
----------+-------------+-------------------------------+-------
 shop     | app_user    | idle                          |     3
 shop     | app_user    | idle in transaction           |     2
 postgres | postgres    | active                        |     1
 shop     | app_user    | idle in transaction (aborted) |     1
 shop     | batch_user  | active                        |     1
 t254     | report_user | idle                          |     1
(6 rows)

セッションの一覧(状態が続いている時間つき)

SELECT pid, usename, datname, client_addr, application_name AS app, state,
       wait_event_type AS wtype, wait_event,
       date_trunc('second', now() - state_change) AS in_state,
       date_trunc('second', now() - xact_start)   AS xact_age,
       left(query, 34) AS query
FROM pg_stat_activity
WHERE backend_type = 'client backend' AND pid <> pg_backend_pid()
ORDER BY state, pid;
 pid  |   usename   | datname | client_addr | app  |             state             |  wtype  | wait_event | in_state | xact_age |             query              
------+-------------+---------+-------------+------+-------------------------------+---------+------------+----------+----------+--------------------------------
 4436 | batch_user  | shop    | 127.0.0.1   | psql | active                        | Timeout | PgSleep    | 00:00:02 | 00:00:02 | select pg_sleep(300);
 4430 | app_user    | shop    | 127.0.0.1   | psql | idle                          | Client  | ClientRead | 00:00:02 |          | select 1;
 4432 | report_user | t254    |             | psql | idle                          | Client  | ClientRead | 00:00:02 |          | select 1;
 4433 | app_user    | shop    | 127.0.0.1   | psql | idle                          | Client  | ClientRead | 00:00:02 |          | select 1;
 4435 | app_user    | shop    | 127.0.0.1   | psql | idle                          | Client  | ClientRead | 00:00:02 |          | select 1;
 4429 | app_user    | shop    | 127.0.0.1   | psql | idle in transaction           | Client  | ClientRead | 00:00:02 | 00:00:02 | insert into t values(1);
 4434 | app_user    | shop    | 127.0.0.1   | psql | idle in transaction           | Client  | ClientRead | 00:00:02 | 00:00:02 | select count(*) from pg_class;
 4431 | app_user    | shop    | 127.0.0.1   | psql | idle in transaction (aborted) | Client  | ClientRead | 00:00:02 |          | select 1/0;
(8 rows)

pid <> pg_backend_pid() は、この確認を実行している自分自身を除く条件です。

使用率を1行で見る(監視向け)

SELECT count(*) FILTER (WHERE state = 'active')                 AS active,
       count(*) FILTER (WHERE state = 'idle')                   AS idle,
       count(*) FILTER (WHERE state LIKE 'idle in transaction%') AS idle_in_tx,
       count(*)                                                 AS total,
       (SELECT setting::int FROM pg_settings WHERE name = 'max_connections') AS max_conn,
       round(100.0 * count(*) /
         (SELECT setting::int FROM pg_settings WHERE name = 'max_connections'), 1) AS used_pct
FROM pg_stat_activity
WHERE backend_type = 'client backend';
 active | idle | idle_in_tx | total | max_conn | used_pct 
--------+------+------------+-------+----------+----------
      1 |    0 |          0 |     1 |       50 |      2.0
(1 row)

(この出力は、後述の上限変更後に、ほかの接続が無い状態で取得したものです。)

一般ロールで見ると列が空になる

pg_stat_activity は、ほかのロールのセッションについては詳細を見せません。 app_user で実行すると次のようになりました。

 pid  |   usename   | state |          query           
------+-------------+-------+--------------------------
 2519 | postgres    |       | <insufficient privilege>
 4432 | report_user |       | <insufficient privilege>
(2 rows)

監視用のロールには GRANT pg_monitor TO report_user; を実行しておくと、スーパーユーザーでなくても state や query を見られます。今回、付与後の report_user から postgres のセッションの active と SQL 文が見えることを確認しました。

too many clients already を再現する

上限 20・スーパーユーザー用の予約 3 のサーバーで、接続を増やしていきます。

一般ロールは17本で受け付けが止まる

app_user の接続を9本追加し、既存の8本と合わせて17本にした状態で、app_user でもう1本つなぎます。

psql -h 127.0.0.1 -p 5432 -U app_user -d shop -c "select 1"
psql: error: connection to server at "127.0.0.1", port 5432 failed: FATAL:  remaining connection slots are reserved for roles with the SUPERUSER attribute

20 − 3 = 17 本で、一般ロールの新しい接続は受け付けられなくなります。 残りの3本はスーパーユーザーが調査や対処のために入るための枠です。

スーパーユーザーも20本で止まる

その予約枠もスーパーユーザーの接続で埋めて、合計20本にしてから接続します。

psql -h 127.0.0.1 -p 5432 -U postgres -d postgres -c "select 1"
psql: error: connection to server at "127.0.0.1", port 5432 failed: FATAL:  sorry, too many clients already

サーバーログにも同じ内容が記録されます。

2026-10-03 17:35:01.132 JST [4552] FATAL:  remaining connection slots are reserved for roles with the SUPERUSER attribute
2026-10-03 17:35:04.319 JST [4749] FATAL:  sorry, too many clients already

(ポート番号は一般的な 5432 に置き換えて表示しています。)

見えたメッセージ 状況 取れる手段
remaining connection slots are reserved ... 一般ロールの枠が満杯。予約枠は空いている スーパーユーザーで接続して、集計 SQL と切断を行える
sorry, too many clients already 予約枠まで満杯 既に開いているスーパーユーザーのセッションから切断する。無ければアプリ側の接続を減らす

アプリケーションをスーパーユーザーで接続させていると、予約枠までアプリが使ってしまいます。 アプリには一般ロールを使い、予約枠を管理作業のために空けておくと、いざというときに入れます。

不要な接続を切断する(pg_cancel_backendとpg_terminate_backend)

2つの関数の違い

関数 動作 接続 向いている場面
pg_cancel_backend(pid) 実行中の SQL を中断する 残る 時間のかかる SQL だけ止めたい
pg_terminate_backend(pid) セッションを終了する 切れる idle や idle in transaction の接続を解放したい

pg_sleep(300) を実行中の batch_user にキャンセルを送った結果です。

SELECT pid, pg_cancel_backend(pid)
FROM pg_stat_activity
WHERE state = 'active' AND usename = 'batch_user';
 pid  | pg_cancel_backend 
------+-------------------
 4436 | t
(1 row)

クライアント側には ERROR: canceling statement due to user request が表示され、セッションは idle に戻りました(接続数は変わりません)。

idle in transaction の接続をまとめて切断する

SELECT pid, usename, pg_terminate_backend(pid) AS terminated
FROM pg_stat_activity
WHERE backend_type = 'client backend'
  AND state IN ('idle in transaction', 'idle in transaction (aborted)')
  AND now() - state_change > interval '3 seconds'
  AND pid <> pg_backend_pid();
 pid  | usename  | terminated 
------+----------+------------
 4434 | app_user | t
 4431 | app_user | t
 4429 | app_user | t
(3 rows)

検証なので「3秒以上」としていますが、ふだんは interval '10 minutes' のように、処理時間に対して十分に長い値を指定します。

idle の接続を条件付きで切断する

SELECT pid, usename, state, pg_terminate_backend(pid) AS terminated
FROM pg_stat_activity
WHERE backend_type = 'client backend'
  AND state = 'idle'
  AND usename = 'app_user'
  AND now() - state_change > interval '3 seconds';

今回は app_user の idle 接続 12 本がすべて t で切断され、残りは idle 4・active 1 になりました。

切断された側のクライアントが次に SQL を送ると、次のように表示されます。

FATAL:  terminating connection due to administrator command
server closed the connection unexpectedly
    This probably means the server terminated abnormally
    before or while processing the request.
connection to server was lost

コネクションプールを使うアプリでは、切断された接続を次に使ったときにこのエラーを受け取ります。 接続の有効性を確認してから貸し出す設定(接続テスト用の SQL や、最大生存時間の設定)があると、自動で張り直されます。

切断に必要な権限

一般ロールが、スーパーユーザーのセッションを切ろうとした結果です。

ERROR:  permission denied to terminate process
DETAIL:  Only roles with the SUPERUSER attribute may terminate processes of roles with the SUPERUSER attribute.
実行するロール 切断できる対象
スーパーユーザー すべてのセッション
pg_signal_backend を付与されたロール スーパーユーザー以外のセッション(一般に知られた仕様。付与コマンドの実行のみ確認)
一般ロール 自分と同じロールのセッション(一般に知られた仕様)

放置された接続を自動で切る(タイムアウト設定)

idle_in_transaction_session_timeout

トランザクションを開いたまま何もしない時間が、指定を超えたら切断します。2秒に設定して、3秒待った結果です。

SET idle_in_transaction_session_timeout = '2s';
BEGIN;
SELECT 1;
-- 3秒待つ
SELECT 2;
FATAL:  terminating connection due to idle-in-transaction timeout
server closed the connection unexpectedly

idle_session_timeout

トランザクションの外で待っているだけの接続(idle)を切断します。PostgreSQL 14 以降で使える設定です(版については一般に知られた仕様)。

SET idle_session_timeout = '2s';
SELECT 1;
-- 3秒待つ
SELECT 2;
FATAL:  terminating connection due to idle-session timeout
server closed the connection unexpectedly

ロール単位・データベース単位で設定する

サーバー全体に掛けるよりも、対象を絞ると影響を読みやすくなります。

ALTER ROLE app_user SET idle_in_transaction_session_timeout = '5min';
ALTER DATABASE shop SET idle_session_timeout = '30min';

SELECT r.rolname, d.datname, s.setconfig
FROM pg_db_role_setting s
LEFT JOIN pg_roles r    ON r.oid = s.setrole
LEFT JOIN pg_database d ON d.oid = s.setdatabase;
 rolname  | datname |                 setconfig                  
----------+---------+--------------------------------------------
 app_user |         | {idle_in_transaction_session_timeout=5min}
          | shop    | {idle_session_timeout=30min}
(2 rows)

この設定は、次に接続したセッションから有効になります。

設定 対象の state 目安の考え方
idle_in_transaction_session_timeout idle in transaction 通常の処理が終わる時間より十分長く。まずはアプリ用ロールに設定
idle_session_timeout idle コネクションプールが接続を保持する時間より長くする。プール側の設定を優先してもよい
statement_timeout active 1つの SQL の実行時間の上限(今回は未実行)

max_connectionsを変更する(ALTER SYSTEMと再起動)

変更前の状態

SELECT name, setting, source, sourcefile, pending_restart
FROM pg_settings
WHERE name IN ('max_connections','reserved_connections');
         name         | setting |       source       |                 sourcefile                  | pending_restart 
----------------------+---------+--------------------+---------------------------------------------+-----------------
 max_connections      | 20      | configuration file | /var/lib/postgresql/16/main/postgresql.conf | f
 reserved_connections | 0       | default            |                                             | f
(2 rows)

ALTER SYSTEMで書き込み、リロードしてみる

ALTER SYSTEM SET max_connections = 50;
ALTER SYSTEM SET reserved_connections = 2;
SELECT pg_reload_conf();

SELECT name, setting, pending_restart
FROM pg_settings
WHERE name IN ('max_connections','reserved_connections');
         name         | setting | pending_restart 
----------------------+---------+-----------------
 max_connections      | 20      | t
 reserved_connections | 0       | t
(2 rows)

リロードでは値が変わらず、pending_restart が t(再起動待ち)になります。 サーバーログにも次のように出ます。

LOG:  parameter "max_connections" cannot be changed without restarting the server
LOG:  parameter "reserved_connections" cannot be changed without restarting the server

ALTER SYSTEM の内容は、データディレクトリの postgresql.auto.conf に書き込まれます。

# Do not edit this file manually!
# It will be overwritten by the ALTER SYSTEM command.
max_connections = '50'
reserved_connections = '2'

なお、SET max_connections = 60; のようにセッションで変えようとすると ERROR: parameter "max_connections" cannot be changed without restarting the server になります。

再起動して反映を確認する

sudo systemctl restart postgresql
# 今回の検証では pg_ctl -D データディレクトリ restart で再起動しました
              name              | setting |       source       |                    sourcefile                    | pending_restart 
--------------------------------+---------+--------------------+--------------------------------------------------+-----------------
 max_connections                | 50      | configuration file | /var/lib/postgresql/16/main/postgresql.auto.conf | f
 reserved_connections           | 2       | configuration file | /var/lib/postgresql/16/main/postgresql.auto.conf | f
 superuser_reserved_connections | 3       | default            |                                                  | f
(3 rows)

postgresql.conf の 20 よりも、postgresql.auto.conf の 50 が優先されました。 「postgresql.conf を書き換えたのに変わらない」ときは、sourcefile 列で「どのファイルの値が使われているか」を確認すると解決が早くなります。また、サーバーの起動コマンドに -c max_connections=20 のような指定があると、そちらが設定ファイルよりも優先されます(今回の検証中に確認)。

変更の方法と再起動の要否

設定・操作 反映方法 再起動
max_connections ALTER SYSTEM または postgresql.conf を編集 必要
superuser_reserved_connections / reserved_connections 同上 必要
idle_in_transaction_session_timeout / idle_session_timeout SET、ALTER ROLE、ALTER DATABASE、ALTER SYSTEM+リロード 不要
ALTER ROLE ... CONNECTION LIMIT 実行した時点から次の接続に適用 不要
ALTER DATABASE ... CONNECTION LIMIT 同上 不要

値を決めるときに確認すること

  • 予約枠の合計は max_connections より小さくします。 max_connections を 4 にして起動を試したところ、superuser_reserved_connections (3) plus reserved_connections (2) must be less than max_connections (4) と表示されて起動しませんでした。値を下げるときは特に注意します。
  • 接続は1本ごとにサーバー側のプロセスを1つ使います。上限を増やすと、同時に動くプロセスが増えたときのメモリ使用量も増えます。 work_mem などの設定と合わせて見直します。
  • スタンバイサーバーがある構成では、スタンバイ側の max_connections をプライマリと同じかそれ以上にします(公式文書の接続設定のページで確認)。
  • 上限を増やす前に、idle や idle in transaction を減らせないかを先に確認します。原因がコミット漏れであれば、上限を増やしても同じ状態に戻ります。

予約接続(superuser_reserved_connectionsとreserved_connections)

PostgreSQL 16 では、スーパーユーザー用の予約とは別に、指定したロール用の予約枠 reserved_connections が加わりました。枠を使えるのは、定義済みロール pg_use_reserved_connections の権限を持つロールです。

検証として、max_connections = 8、superuser_reserved_connections = 3、reserved_connections = 2 にし、report_user に権限を付けました。一般ロールが使えるのは 8 − 3 − 2 = 3 本です。

GRANT pg_use_reserved_connections TO report_user;

app_user で3本つないだあとの結果です。

接続を試したロール 結果
app_user(4本目) FATAL: remaining connection slots are reserved for roles with privileges of the "pg_use_reserved_connections" role
report_user(予約枠の1本目) 接続できた
report_user(予約枠2本を使い切ったあと) FATAL: remaining connection slots are reserved for roles with the SUPERUSER attribute
postgres(スーパーユーザー) 接続できた
枠 設定 既定値 使えるロール
一般の枠 max_connections から2つの予約を引いた数 100 − 3 − 0 = 97 すべてのロール
ロール用の予約枠(16以降) reserved_connections 0 pg_use_reserved_connections の権限を持つロールとスーパーユーザー
スーパーユーザー用の予約枠 superuser_reserved_connections 3 スーパーユーザー

監視ツールや管理作業用のロールに予約枠を割り当てておくと、スーパーユーザーの権限を渡さなくても、混雑時に状況を確認できます。

ロール・データベース単位の上限(CONNECTION LIMIT)

特定のロールが接続を使いすぎないようにするには、ロールやデータベースに上限を付けます。

ALTER ROLE batch_user CONNECTION LIMIT 1;
ALTER DATABASE t254 CONNECTION LIMIT 1;
SELECT rolname, rolconnlimit FROM pg_roles WHERE rolname IN ('app_user','batch_user');
  rolname   | rolconnlimit 
------------+--------------
 app_user   |           -1
 batch_user |            1
(2 rows)

−1 は「制限なし」です。上限を超えて接続すると次のようになりました。

FATAL:  too many connections for role "batch_user"
FATAL:  too many connections for database "t254"

PgBouncerの導入を検討する目安

PgBouncer は、アプリケーションと PostgreSQL の間に入り、少ない本数のサーバー接続を多くのクライアントで共有する接続プーラーです。この記事では PgBouncer 自体は未実行で、判断の目安と一般的な設定例だけを載せます。

状況 まず行うこと PgBouncer の検討
idle が大半で、active は少ない アプリ側のプールの最大数・最小数を見直す アプリのプロセス数が多く、合計が上限を超えるなら有力
idle in transaction が多い コミット漏れの修正、タイムアウト設定 先にアプリ側を直す。プーラーでは解決しにくい
active が常に多い SQL やインデックスの見直し、サーバーの増強 待ち行列を作る用途では役立つ
サーバーレスや多数のワーカーから短い接続が大量に来る — 向いている
アプリ側に十分なプールがあり、合計が上限に収まる 現状のままで問題ない 急がなくてよい

計算の例として、アプリケーションのプロセスが 8 個、それぞれのプールの最大が 20 本なら、最大 160 本の接続が張られる可能性があります。これが max_connections の一般枠を超えるなら、プールの最大数を下げるか、プーラーで束ねるかを選びます。

設定例(未実行。値は例です):

[databases]
shop = host=127.0.0.1 port=5432 dbname=shop

[pgbouncer]
listen_port = 6432
pool_mode = transaction
max_client_conn = 500
default_pool_size = 20

pool_mode = transaction は、トランザクションごとにサーバー接続を貸し出すモードです。セッションをまたいで状態を持つ機能(セッション単位の SET など)を使っている場合は、動作を確認してから切り替えます。

再発防止のチェックリスト

確認項目 確認方法
アプリは一般ロールで接続しているか SELECT usename, count(*) ... で postgres が多くないか
application_name を設定しているか 接続文字列に application_name=... を入れる
プールの最大数 × プロセス数が一般枠に収まるか 残り接続数の SQL で計算
idle in transaction が長く残っていないか state と state_change を定期的に確認
タイムアウトをアプリ用ロールに設定したか pg_db_role_setting を確認
使用率を監視しているか 使用率の SQL を監視ツールに登録(例:80% で通知)
管理用の予約枠を確保しているか superuser_reserved_connections / reserved_connections

よくある質問

Q. pg_stat_activity の件数と max_connections を比べてよいですか? A. backend_type = 'client backend' で絞った件数と比べてください。条件なしだと autovacuum launcher などの内部プロセスも数に入ります(今回は 14 行のうち 9 行がクライアント接続でした)。

Q. max_connections を変更したのに反映されません。 A. 再起動が必要な設定です。リロードだけでは pending_restart が t になります。再起動後も変わらないときは、pg_settings の sourcefile 列でどのファイルの値が使われているかを確認します。

Q. idle の接続は切断したほうがよいですか? A. コネクションプールが保持している idle は正常な状態です。上限に近づいているときだけ、プールの最大数の見直しや idle_session_timeout を検討します。優先して調べたいのは idle in transaction です。

Q. too many clients already が出て、スーパーユーザーでも入れません。 A. 既に開いている管理用のセッションがあれば、そこから pg_terminate_backend で切断します。無い場合はアプリケーション側を止めて接続を減らします。その後、アプリを一般ロールで接続させて予約枠を空けておくと、次回は入れます。

まとめ

  • 現在の接続数は backend_type = 'client backend' で絞って数える。上限は SHOW max_connections;。
  • 一般ロールが使えるのは「max_connections − 予約枠」まで。上限 20・予約 3 なら 17 本で受け付けが止まる。
  • state 別に集計して対処を決める。idle はプール設定、idle in transaction はコミット漏れ、active は SQL を見直す。
  • SQL だけ止めるなら pg_cancel_backend、接続を切るなら pg_terminate_backend。
  • idle_in_transaction_session_timeout をアプリ用ロールに設定しておくと、放置トランザクションが自動で片付く。
  • max_connections の変更は再起動が必要。pending_restart と sourcefile で反映状況を確認できる。
  • 16以降は reserved_connections で監視・管理用ロールの枠を確保できる。接続が多い構成では PgBouncer も選択肢になる。

株式会社RJC ― SI事業・SES事業・AI駆動開発。RJCは一緒に成長を楽しめる会社です。

WE ARE HIRING

RJCで一緒に開発しながら、
成長を楽しみませんか?

RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!

  • 127日年間休日
  • 12時間平均残業時間
  • 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!

ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。

ABOUT RJC RJCがどんな会社か知る 考え方、研修、働き方、福利厚生、社員の前職まで。RJCのことが丸わかり! RJC丸わかりページへ JOB DESCRIPTION 仕事内容・待遇を見てみる 仕事内容、給与・待遇、選考の流れ。経験者も未経験も!応募前に知りたいこと、まとめました! 募集要項を見る ENTRY エントリーする エントリーは1〜2分・履歴書不要です。まずは話を聞いてみたい、という方でも歓迎です! エントリーフォームへ

RJCで一緒に開発しながら、 成長を楽しみませんか?