目标
锁队列看起来像一百个生病的请求,其实往往是一个看似健康的反派:一个握着锁却什么也不做的会话。pg_blocking_pids 的自连接把堆积的等待者变成一张“谁阻塞谁、阻塞了多久”的清单。链条头部一旦可见,等待、终止还是改代码的决策就一目了然,空闲事务超时则防止下一个被遗弃的会话重演事故。
步骤要点
- 用 pg_stat_activity 自连接 pg_blocking_pids 列出每对“等待者-阻塞者”、阻塞查询与持锁时长。
- 统计 active 会话数量,量化排队规模是否远超基线。
- 检查阻塞者状态:idle in transaction 长达 20 分钟的会话基本是被遗忘的事务,复核后可安全终止。
- 确认无误后执行 pg_terminate_backend(
),等待者立即继续,API 连接压力回落。 - 给应用角色设 idle_in_transaction_session_timeout=60s、系统级 lock_timeout=10s,让被遗弃事务自动清场、锁等待响亮失败。
排错提示
pg_blocking_pids 为空但仍在等,可能是咨询锁或轻量锁,去查 granted=false 的 pg_locks 与等待者的 wait_event;同一会话反复成为阻塞者,说明该代码路径在事务内做慢速外部 I/O,应把外部调用移出事务;终止会回滚其工作,先确认事务确被遗弃,拿不准就给受害者配 lock_timeout 并对阻塞者运行时长告警。
适用边界
查看活动视图需要监控权限,应用角色通常没有;终止后端是应急手段而非修复,事务范围缺陷仍需代码变更。
代码示例
SELECT blocked.pid AS waiting_pid,
blocking.pid AS blocking_pid,
now() - blocking.query_start AS held_for
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking
ON blocking.pid = ANY(pg_blocking_pids(blocked.pid));