如何检测Postgres中持有锁的查询?

时间:2014-10-21 14:30:54

标签: sql postgresql locking

我想不断跟踪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;

然后它返回等待获取锁的查询。但我无法改变它,以便它可以返回阻塞和阻止查询。

有什么想法吗?

6 个答案:

答案 0 :(得分:40)

自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;

答案 1 :(得分:22)

从这个excellent article on query locks in 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;

由于查询很长但很有用,文章作者为它创建了一个视图,以简化它的使用。

答案 2 :(得分:3)

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 :(得分:1)

我发现通常缺少的一件事就是能够查找行锁。至少在我工作过的较大的数据库中,行锁没有显示在pg_locks中(如果是,pg_locks会更大,更大,并且没有真正的数据类型来正确显示该视图中的锁定行)

我不知道有一个简单的解决方案,但我通常会查看锁等待的表,并搜索xmax小于其中存在的事务ID的行。这通常给我一个开始的地方,但它有点实际操作,而不是自动化。

注意,这表示您对这些表上的行进行了未提交的写入操作。提交后,行在当前快照中不可见。但对于大型餐桌来说,这是一种痛苦。

答案 4 :(得分:1)

对于postgresql 9.6之前的没有pg_blocking_pids函数的postgresql,可以使用下面的查询来查找阻塞查询和阻塞查询。

SELECT w.query                          AS waiting_query,
       w.pid                            AS waiting_pid,
       w.usename                        AS waiting_user,
       now() - w.query_start            AS waiting_duration,
       l.query                          AS locking_query,
       l.pid                            AS locking_pid,
       l.usename                        AS locking_user,
       t.schemaname || '.' || t.relname AS tablename,
       now() - l.query_start            AS locking_duration
FROM pg_stat_activity w
         JOIN pg_locks l1 ON w.pid = l1.pid AND NOT l1.granted
         JOIN pg_locks l2 ON l1.relation = l2.relation AND l2.granted
         JOIN pg_stat_activity l ON l2.pid = l.pid
         JOIN pg_stat_user_tables t ON l1.relation = t.relid
WHERE w.waiting;

答案 5 :(得分:0)

Postgres具有通过SQL表公开的非常丰富的系统目录。 PG的统计信息收集器是一个子系统,支持收集和报告有关服务器活动的信息。

现在要找出阻塞的PID,您只需查询pg_stat_activity

select pg_blocking_pids(pid) as blocked_by
from pg_stat_activity
where cardinality(pg_blocking_pids(pid)) > 0;

要获取与阻塞PID对应的查询,您可以自联接或将其用作子查询中的where子句。

SELECT query
FROM pg_stat_activity
WHERE pid IN (select unnest(pg_blocking_pids(pid)) as blocked_by from pg_stat_activity where cardinality(pg_blocking_pids(pid)) > 0);

注意:由于pg_blocking_pids(pid)返回一个Integer [],因此在unnest子句中使用它之前,您需要先WHERE pid IN对其进行补偿。

寻找慢速查询有时可能很乏味,因此要耐心等待。狩猎愉快。