跳转到主要内容

Lock 等待

通常可以直接从 SQL 找到持有者与阻塞链的重量级锁。

Lock 是最容易直接处置的一类:后端向锁管理器申请重量级锁,但不兼容的持有者尚未释放。与 LWLock 不同,这些等待通常在 pg_locks 中有对应行,pg_blocking_pids() 也能给出有效答案。

看阻塞图,不要只数受害者

二十个等待者可能都只是一个空闲事务的症状。先走到阻塞图最前端,再判断持有者是在做有效工作、已被遗弃,还是处于计划内 DDL/发布窗口。

  • 正常: 普通写入或计划内 DDL 期间,亚秒级的锁交接。
  • 关注: 任一面向用户的等待超过其延迟目标,或至少 10% 的活动会话等待同一个根阻塞者。
  • 紧急: 根持有者是 idle in transaction、阻塞链持续增长、关键 DDL 阻塞流量,或死锁开始增加。

值得先认出的事件

事件 通常表示
relation 表/索引级锁冲突,常见于 DDL 对 DML
transactionid 等待另一事务结束,常见于并发更新同一行
tuple 元组锁竞争或很长的行锁队列
extend 多个会话在扩展同一关系时串行化
virtualxid DDL 等待可能仍在使用对象的事务
advisory 应用自定义的 advisory lock 协调

阻塞者查询

SELECT w.pid AS waiting_pid, b.pid AS blocking_pid,
       w.wait_event, now() - w.query_start AS waiting_for,
       now() - b.xact_start AS blocker_xact_age,
       b.state AS blocker_state,
       left(w.query, 100) AS waiting_query,
       left(b.query, 100) AS blocking_query
FROM pg_stat_activity AS w
CROSS JOIN LATERAL unnest(pg_blocking_pids(w.pid)) AS x(pid)
JOIN pg_stat_activity AS b ON b.pid = x.pid
ORDER BY waiting_for DESC;

常见误读

  • 被列为阻塞者的会话并不一定可以安全终止;它可能是维护业务不变量的唯一写入者。
  • transactionid 不代表事务 ID 即将耗尽。
  • relation 本身不说明具体关系;需要把 pg_locks.relation 关联到 pg_class
  • 增大 lock_timeout 只改变受害者等多久,并不会移除阻塞者。

等待获取用户 advisory lock。

等待获取逻辑复制订阅端正在应用的远程事务上的锁。

等待扩展 relation。

等待更新 pg_database.datfrozenxid 和 pg_database.datminmxid。

等待获取非 relation 数据库对象上的锁。

等待获取 relation 页面上的锁。

等待获取 relation 上的锁。

等待获取推测式插入锁。

等待获取 tuple 上的锁。