PostgreSQL 12 min read

PostgreSQLで実行中のクエリを止める ― pg_cancel_backendとpg_terminate_backendの違い

※この記事にはアフィリエイト広告(プロモーション)が含まれます。

長く終わらないクエリや、他のセッションを待たせているセッションは、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の起動・停止・再起動

PostgreSQL をもっと深く知るための本

PostgreSQL実践入門 ──アーキテクチャ、運用監視、性能改善

技術評論社・2026年2月

アーキテクチャから運用監視・性能改善までを扱う。付録に PostgreSQL 18 の新機能と設定項目の差分がある。

Amazonで見る

[改訂3版]内部構造から学ぶPostgreSQL ―設計・運用計画の鉄則

技術評論社・2022年11月・PostgreSQL 14 ベース

内部構造の理解から、設計と運用計画の立て方を学ぶ。

Amazonで見る

Amazonのアソシエイトとして、FIRE SE は適格販売により収入を得ています。