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

Rom*_*man 40 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;
Run Code Online (Sandbox Code Playgroud)

不幸的是,它永远不会返回非空结果集.如果我将给定查询简化为以下形式:

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;
Run Code Online (Sandbox Code Playgroud)

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

有任何想法吗?

a_h*_*ame 53

从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;
Run Code Online (Sandbox Code Playgroud)

  • 我总是来这个答案来复制查询。感谢你的回答 (13认同)

Dev*_*evi 31

这篇关于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;
Run Code Online (Sandbox Code Playgroud)

由于查询很长但很有用,因此文章作者为其创建了一个视图,以简化其用法.

  • 需要注意的是,“blocking_query”并不一定会显示导致阻塞的查询。它仅显示阻塞事务中执行的最后一个查询。 (6认同)

M. *_*put 23

如何显示所有被阻止的查询。

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;
Run Code Online (Sandbox Code Playgroud)

您可以使用以下命令终止被阻止的查询。

SELECT pg_cancel_backend(a.pid), pg_terminate_backend(a.pid);
Run Code Online (Sandbox Code Playgroud)

您可以使用它终止所有被阻止的查询。

SELECT pg_cancel_backend(a.pid), pg_terminate_backend(a.pid)
FROM( 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) a
Run Code Online (Sandbox Code Playgroud)


小智 8

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;
Run Code Online (Sandbox Code Playgroud)

要获取阻塞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);
Run Code Online (Sandbox Code Playgroud)

注意:由于pg_blocking_pids(pid)返回一个 Integer[],所以unnestWHERE pid IN子句中使用它之前需要它。

寻找缓慢的查询有时会很乏味,所以要有耐心。狩猎快乐。


小智 8

对于早于 postgresql 9.6 的 postgresql 版本,它没有pg_blocking_pids函数\xef\xbc\x8c,您可以使用以下查询来查找阻塞查询和阻塞查询。

\n
SELECT w.query                          AS waiting_query,\n       w.pid                            AS waiting_pid,\n       w.usename                        AS waiting_user,\n       now() - w.query_start            AS waiting_duration,\n       l.query                          AS locking_query,\n       l.pid                            AS locking_pid,\n       l.usename                        AS locking_user,\n       t.schemaname || \'.\' || t.relname AS tablename,\n       now() - l.query_start            AS locking_duration\nFROM pg_stat_activity w\n         JOIN pg_locks l1 ON w.pid = l1.pid AND NOT l1.granted\n         JOIN pg_locks l2 ON l1.relation = l2.relation AND l2.granted\n         JOIN pg_stat_activity l ON l2.pid = l.pid\n         JOIN pg_stat_user_tables t ON l1.relation = t.relid\nWHERE w.waiting;\n
Run Code Online (Sandbox Code Playgroud)\n


jpm*_*c26 6

a_horse_with_no_name答案的这种修改将为您提供阻塞查询,而不仅仅是阻塞的会话:

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));
Run Code Online (Sandbox Code Playgroud)

  • 请注意,这不会给您阻塞查询。它只会显示该会话中执行的最后一个查询。那**不一定**是导致锁定的查询!例如,如果会话运行锁定一行的 UPDATE,但未提交,然后运行 ​​SELECT,则 SELECT 将显示在“pg_stat_activity”中,但锁定实际上是由 UPDATE 引起的 (3认同)

Ina*_*asa 5

其他人已经回答了您的查询,但我遇到了某些情况:有很多查询相互阻塞,我想找到每个被阻止会话的主要阻止程序会话。

第一个例子:会话 5 被会话 4 阻塞,4 正在等待 3 和 2,而 3 和 2 正在等待会话 1。

5->4->{3,2}->1
Run Code Online (Sandbox Code Playgroud)

在这种情况下,第一阶段是真正的问题,一旦解决了,其他人就会照顾好自己。

所以我想要一个查询,它将显示如下结果:

被阻止的 pid 拦截器 pid
5 1
4 1
3 1
2 1

因此,如果存在任何链锁定会话,此查询将向您显示每个被阻止会话的主要阻止程序会话。

;with recursive 
    find_the_source_blocker as (
        select  pid
               ,pid as blocker_id
        from pg_stat_activity pa
        where pa.state<>'idle'
              and array_length(pg_blocking_pids(pa.pid), 1) is null

        union all

        select              
                t.pid  as  pid
               ,f.blocker_id as blocker_id
        from find_the_source_blocker f 
        join (  SELECT
                    act.pid,
                    blc.pid AS blocker_id
                FROM pg_stat_activity AS act
                LEFT JOIN pg_stat_activity AS blc ON blc.pid = ANY(pg_blocking_pids(act.pid))
                where act.state<>'idle') t on f.pid=t.blocker_id
        )
    
select distinct 
       s.pid
      ,s.blocker_id
      ,pb.usename       as blocker_user
      ,pb.query_start   as blocker_start
      ,pb.query         as blocker_query
      ,pt.query_start   as trans_start
      ,pt.query         as trans_query
from find_the_source_blocker s
join pg_stat_activity pb on s.blocker_id=pb.pid
join pg_stat_activity pt on s.pid=pt.pid
where s.pid<>s.blocker_id
Run Code Online (Sandbox Code Playgroud)


Chr*_*ers 4

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

我不知道有一个简单的解决方案,但通常我所做的是查看锁正在等待的表并搜索 xmax 小于那里存在的事务 id 的行。这通常为我提供了一个起点,但它需要动手操作,而且不适合自动化。

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

  • pgrowlocks 扩展将为您提供行级锁:https://www.postgresql.org/docs/9.6/static/pgrowlocks.html (5认同)