wait_event_type显示的是进程当前等待类型(如client、io),并非阻塞源;定位阻塞需结合pg_blocking_pids()或pg_locks中granted=false等字段分析。

查 wait_event_type 时为什么总看到 Client 或 IO 却没发现锁?
因为 wait_event_type 描述的是「当前正在等什么」,不是「谁在阻塞你」。它只反映 PostgreSQL 后端进程的即时等待状态,和锁链无关——比如 Client 表示在等客户端发下一条命令,IO 可能只是刷 WAL 或读数据页,这些都不是阻塞源。
真正要定位阻塞,得结合 pg_blocking_pids(pid) 或自连 pg_stat_activity 查 blocking_pid 字段(14+ 版本);老版本只能靠 pg_locks 关联 granted = false 和 transactionid 倒推。
-
wait_event_type是“症状”,不是“病因”;别把它当锁表依据 - 常见误判:
Lock类型确实可疑,但Lock+wait_event = 'relation'才大概率是 DDL 阻塞,而Lock+'transactionid'多是长事务未提交 - 如果
wait_event_type是Client,先看state = 'idle in transaction'—— 这才是隐藏杀手,它不等资源,但会一直占着锁
用 pg_stat_activity 搭配 pg_locks 写阻塞诊断 SQL 时字段怎么对齐?
核心是把 pg_stat_activity.pid 和 pg_locks.pid 关联,但要注意:10+ 版本 pg_locks.pid 是持有锁或等待锁的 backend pid,而 pg_stat_activity 的 pid 是唯一标识,可直接 join;9.6 及更早需用 pg_locks.virtualxid 或 transactionid 间接关联,逻辑更绕。
一个稳妥的诊断模板(PostgreSQL 12+):
SELECT blocked.pid AS blocked_pid,
blocked.query AS blocked_query,
blocking.pid AS blocking_pid,
blocking.query AS blocking_query,
blocked.wait_event_type,
blocked.wait_event
FROM pg_stat_activity blocked
JOIN pg_stat_activity blocking ON blocking.pid = ANY(pg_blocking_pids(blocked.pid))
WHERE blocked.state = 'active' AND blocked.wait_event IS NOT NULL;- 必须加
blocked.state = 'active',否则会拉出一堆idle进程干扰判断 -
pg_blocking_pids()返回数组,要用= ANY(...),不能用= - 如果查不到 blocking 进程,可能是它已退出但事务未结束(如 crash 后未清理),此时要去
pg_locks查granted = false的行,并找对应virtualxid的最早持有者
wait_event_type = 'Lock' 但 wait_event 是空值,怎么回事?
这是 PostgreSQL 14 引入的行为:当等待类型为 Lock,但具体锁对象尚未确定(比如正处在锁升级、或锁管理器内部路径中),wait_event 就留空。它不代表没锁,反而说明锁竞争发生在更底层,比如 tuple 级锁升级为 page 锁时的中间态。
- 遇到空
wait_event且wait_event_type = 'Lock',优先检查是否有大量UPDATE/DELETE在同一张小表上高频执行——容易触发锁升级争用 - 对比
pg_locks.locktype:如果对应行的locktype = 'tuple'且mode = 'RowExclusiveLock',但granted = false,基本可断定是热点行锁冲突 - 这种空值不会出现在
pg_stat_activity的旧版本(13 及以前),所以升级后突然看到空值,别慌,是行为变更,不是数据损坏
为什么在 RDS 或 Aurora 上查不到真实的 blocking_pid?
云厂商常屏蔽或重写 pg_stat_activity 中的敏感字段。例如 AWS RDS 默认关闭 rds.force_ssl 以外的某些权限,且 blocking_pid 列在多数 RDS 版本里始终为 NULL;Aurora 更进一步,用自研锁管理器,pg_blocking_pids() 返回空数组是常态。
- 替代方案:用
SELECT * FROM pg_locks WHERE NOT granted ORDER BY pid;,再人工比对database、relation、transactionid字段,找「有锁没被授」的源头 - RDS 上可以开启
log_lock_waits = on,配合deadlock_timeout抓日志,虽然滞后但信息更全 - 别依赖
pg_stat_activity的单次快照——云环境锁可能秒级释放,建议用pg_stat_activity+pg_locks联合视图每 5 秒采样一次,存到临时表再分析
真实阻塞链往往跨多个事务、涉及隐式锁升级和云平台抽象层,光盯 wait_event_type 容易漏掉中间环节。尤其当 wait_event 为空、或 blocking_pid 不可见时,得切到 pg_locks 底层字段一层层剥。










