【问题标题】:How to detect query which holds the lock in Postgres?如何检测在 Postgres 中持有锁的查询?
【发布时间】:2014-12-16 19:52:32
【问题描述】:

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

然后它返回正在等待获取锁的查询。但我无法设法更改它,以便它可以返回被阻止和阻止的查询。

有什么想法吗?

【问题讨论】:

  • 什么是拦截器查询?这是一个持有锁的事务,获取它的特定查询可能在该事务中完成并消失,而锁仍然被持有。
  • 听起来很合理,但 Locks Monitoring 文章的作者在这种情况下是什么意思?
  • 只显示行级锁。我发现这个更有用(虽然更复杂),因为它也显示了对象级锁:wiki.postgresql.org/wiki/Lock_dependency_information

标签: sql postgresql locking


【解决方案1】:

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

【讨论】:

  • 我总是来这个答案来复制查询。感谢您的回答
  • Devi 下面的回答包含此信息以及其他信息。
【解决方案2】:

从这个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;

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

【讨论】:

  • “错误:列blockeda.pid 不存在” ... mmh 哪个版本?
  • 那是在 9.6.2
  • 同样适用于 9.3。
【解决方案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));

【讨论】:

    【解决方案4】:

    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[],所以在WHERE pid IN子句中使用它之前需要unnest

    寻找慢速查询有时会很乏味,所以请耐心等待。狩猎愉快。

    【讨论】:

    • 你不需要unnest()你可以把它简化成where pid = any(pg_blocking_pids(pid))
    【解决方案5】:

    对于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;
    

    【讨论】:

      【解决方案6】:

      我发现其中经常缺少的一件事是查找行锁的能力。至少在我处理过的大型数据库中,行锁不会显示在 pg_locks 中(如果是,pg_locks 会大得多,并且没有真正的数据类型可以在该视图中正确显示锁定的行)。

      我不知道有一个简单的解决方案,但通常我所做的是查看锁正在等待的表并搜索 xmax 小于那里存在的事务 ID 的行。这通常给了我一个开始的地方,但它有点动手操作,对自动化不友好。

      请注意,这表明您对这些表上的行进行了未提交的写入。一旦提交,这些行在当前快照中不可见。但对于大桌子来说,这很痛苦。

      【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多