web-dev-qa-db-ja.com

Postgresでロックを保持しているクエリを検出する方法は?

Postgresの相互ロックを常に追跡したい。

Locks Monitoring の記事に出会い、次のクエリを実行しようとしました。

SELECT bl.pid     AS blocked_pid,
     a.usename  AS blocked_user,
     kl.pid     AS blocking_pid,
     ka.usename AS blocking_user,
     a.query    AS blocked_statement
FROM  pg_catalog.pg_locks         bl
 JOIN pg_catalog.pg_stat_activity a  ON a.pid = bl.pid
 JOIN pg_catalog.pg_locks         kl ON kl.transactionid = bl.transactionid AND kl.pid != bl.pid
 JOIN pg_catalog.pg_stat_activity ka ON ka.pid = kl.pid
WHERE NOT bl.granted;

残念ながら、空でない結果セットを返すことはありません。特定のクエリを次の形式に単純化すると:

SELECT bl.pid     AS blocked_pid,
     a.usename  AS blocked_user,
     a.query    AS blocked_statement
FROM  pg_catalog.pg_locks         bl
 JOIN pg_catalog.pg_stat_activity a  ON a.pid = bl.pid
WHERE NOT bl.granted;

次に、ロックの取得を待機しているクエリを返します。しかし、ブロックされたクエリとブロッカークエリの両方を返すように変更することはできません。

何か案は?

36
Roman

9.6以降、これは別のセッションをブロックしているセッションを見つけるために関数pg_blocking_pids()を導入したため、はるかに簡単です。

したがって、次のようなものを使用できます。

select pid, 
       usename, 
       pg_blocking_pids(pid) as blocked_by, 
       query as blocked_query
from pg_stat_activity
where cardinality(pg_blocking_pids(pid)) > 0;
50

これから Postgresのクエリロックに関する優れた記事 から、ブロックされたクエリとブロッカークエリ、および次のクエリからの情報を取得できます。

CREATE VIEW lock_monitor AS(
SELECT
  COALESCE(blockingl.relation::regclass::text,blockingl.locktype) as locked_item,
  now() - blockeda.query_start AS waiting_duration, blockeda.pid AS blocked_pid,
  blockeda.query as blocked_query, blockedl.mode as blocked_mode,
  blockinga.pid AS blocking_pid, blockinga.query as blocking_query,
  blockingl.mode as blocking_mode
FROM pg_catalog.pg_locks blockedl
JOIN pg_stat_activity blockeda ON blockedl.pid = blockeda.pid
JOIN pg_catalog.pg_locks blockingl ON(
  ( (blockingl.transactionid=blockedl.transactionid) OR
  (blockingl.relation=blockedl.relation AND blockingl.locktype=blockedl.locktype)
  ) AND blockedl.pid != blockingl.pid)
JOIN pg_stat_activity blockinga ON blockingl.pid = blockinga.pid
  AND blockinga.datid = blockeda.datid
WHERE NOT blockedl.granted
AND blockinga.datname = current_database()
);

SELECT * from lock_monitor;

クエリは長くても便利なので、記事の作成者はクエリを簡単に使用できるようにビューを作成しました。

30
Devi

a_horse_with_no_name's answer のこの変更により、ブロックされたセッションだけでなく、ブロッククエリも提供されます。

SELECT
    activity.pid,
    activity.usename,
    activity.query,
    blocking.pid AS blocking_id,
    blocking.query AS blocking_query
FROM pg_stat_activity AS activity
JOIN pg_stat_activity AS blocking ON blocking.pid = ANY(pg_blocking_pids(activity.pid));
3
jpmc26

私はこれらからしばしば欠けていると思うことの1つは、行ロックを検索する機能です。少なくとも私が取り組んできたより大きなデータベースでは、行ロックはpg_locksに表示されません(もしそうなら、pg_locksははるかに大きくなり、そのビューにロックされた行を適切に表示する実際のデータ型はありません)。

これに対する簡単な解決策があることはわかりませんが、通常、ロックが待機しているテーブルを見て、xmaxがトランザクションIDよりも小さい行を検索します。通常、それは開始する場所を提供しますが、それは少し実践的であり、自動化に適していません。

これらのテーブルの行に対するコミットされていない書き込みを示していることに注意してください。コミットすると、行は現在のスナップショットに表示されなくなります。しかし、大きなテーブルの場合、それは苦痛です。

1
Chris Travers