我想不断跟踪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;
然后它返回等待获取锁的查询。但我无法改变它,以便它可以返回阻塞和阻止查询。
有什么想法吗?
答案 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
对其进行补偿。
寻找慢速查询有时可能很乏味,因此要耐心等待。狩猎愉快。