※この記事にはアフィリエイト広告(プロモーション)が含まれます。
長く終わらないクエリや、他のセッションを待たせているセッションは、pg_stat_activity で PID を調べて止めます。この記事の出力は PostgreSQL 18.6(Ubuntu 26.04)で採りました。
まず見る(結論)
SELECT pid, usename, state, wait_event_type, pg_blocking_pids(pid) AS blocked_by,
now() - query_start AS elapsed, left(query, 45) AS query
FROM pg_stat_activity
WHERE backend_type = 'client backend' AND pid <> pg_backend_pid()
ORDER BY pid;
SELECT pg_cancel_backend(2219); -- 実行中の文だけ止める。接続は残る
SELECT pg_terminate_backend(2255); -- 接続ごと切る
idle in transactionのセッションにpg_cancel_backendは効かない。tが返るのにロックは解放されなかった- 他のユーザーのクエリを止めるには
pg_signal_backendロールが要る。スーパーユーザーのセッションはスーパーユーザーにしか止められない
実行中のクエリを探す
別のセッションで SELECT pg_sleep(30); を実行中に、上の SQL を流しました。
pid | usename | state | wtype | wait_event | blocked_by | elapsed | query
------+---------+--------+---------+------------+------------+----------+----------------------
2219 | app1 | active | Timeout | PgSleep | {} | 00:00:01 | SELECT pg_sleep(30);
| 列 | 見るところ |
|---|---|
state |
active=実行中、idle=待機中、idle in transaction=トランザクションを開いたまま何もしていない |
wait_event_type |
Lock ならロック待ち |
pg_blocking_pids(pid) |
このセッションを待たせている PID |
query_start / xact_start |
今の文の開始時刻/トランザクションの開始時刻 |
query |
実行中の文。idle 系では最後に実行した文 |
一般ユーザーで実行すると、上の SQL には自分のロールの行しか出ませんでした。pg_read_all_stats ロールを付与すると、他のユーザーの行も出ました。
pg_cancel_backend と pg_terminate_backend
SELECT pid, pg_cancel_backend(pid) FROM pg_stat_activity WHERE usename = 'app1' AND state = 'active';
pid | pg_cancel_backend
------+-------------------
2219 | t
止められた側の psql には以下のように出て、同じ接続のまま次の文が実行されました。
ERROR: canceling statement due to user request
after_cancel | alive
-----------------+-------
still connected | t
pg_terminate_backend では接続が切られ、次の文は実行されませんでした。
FATAL: terminating connection due to administrator command
SSL connection has been closed unexpectedly
error: connection to server was lost
存在しない PID を渡すと WARNING: PID 999999 is not a PostgreSQL backend process が出て f が返ります。
サーバー上で kill -INT <PID> を送っても pg_cancel_backend と同じく canceling statement due to user request が出ました。ps では postgres: 18/main: app1 fsv 127.0.0.1(33870) SELECT のように、ユーザー・DB・接続元が表示されます。
idle in transaction がロックを握っている
app1 が BEGIN; UPDATE acct SET bal = bal + 1 WHERE id = 1; のままコミットせず、app2 が同じ行を更新しようとした状態です。
pid | usename | state | wtype | wait_event | blocked_by | elapsed | query
------+---------+---------------------+--------+---------------+------------+----------+---------------------------------------------
2255 | app1 | idle in transaction | Client | ClientRead | {} | 00:00:04 | UPDATE acct SET bal = bal + 1 WHERE id = 1;
2259 | app2 | active | Lock | transactionid | {2255} | 00:00:02 | UPDATE acct SET bal = bal + 10 WHERE id = 1
2259 は blocked_by が {2255} で、2255 を待っています。
2255 に pg_cancel_backend を実行すると t が返りましたが、1秒後も状態は同じでした。止める対象の実行中の文が無いためです。pg_terminate_backend(2255) で接続を切ると、2259 の UPDATE がすぐに終わりました(待ち時間を含めて 3.2 秒)。app1 の更新はロールバックされ、bal は app2 の更新分だけ増えて 110 でした。
切られた側のアプリケーションにはエラーが返ります。誰のどんな処理かを usename・application_name・client_addr で確かめてから切ります。
権限
| 止める側 | 止められる側 | 結果 |
|---|---|---|
| app1 | app1(自分の別セッション) | t |
| app1 | app2 | ERROR: permission denied to cancel query |
app1(pg_signal_backend 付与後) |
app2 | t |
app1(pg_signal_backend 付与後) |
postgres(スーパーユーザー) | ERROR: permission denied to cancel query |
エラーの DETAIL は、app2 に対しては Only roles with privileges of the role whose query is being canceled or with privileges of the "pg_signal_backend" role may cancel this query.、スーパーユーザーに対しては Only roles with the SUPERUSER attribute may cancel queries of roles with the SUPERUSER attribute. でした。スーパーユーザーのセッションに pg_terminate_backend を使った場合は ERROR: permission denied to terminate process でした。
自動的に止める
SET statement_timeout = '2s';
SELECT pg_sleep(5);
ERROR: canceling statement due to statement timeout -- 2.000 秒で止まった
ALTER ROLE app1 SET statement_timeout = '3s'; -- 次の接続から app1 の既定値になる
ALTER ROLE app1 RESET statement_timeout; -- 戻す
コミットし忘れのセッションは idle_in_transaction_session_timeout で切れます。'3s' にして BEGIN; SELECT 1; の後に5秒放置してから次の文を実行すると、以下のように出て接続が切れていました。
FATAL: terminating connection due to idle-in-transaction timeout
statement_timeout・idle_in_transaction_session_timeout・transaction_timeout の既定値はどれも 0(無効)でした。止めたときのメッセージはサーバーログ(/var/log/postgresql/postgresql-18-main.log)にも app1@fsv ERROR: canceling statement due to statement timeout の形で残ります。
関連: PostgreSQLの「deadlock detected」 / PostgreSQLの実行計画の見方(EXPLAIN) / PostgreSQLの起動・停止・再起動