SQL pg_stat_activity 的 wait_event_type 与阻塞诊断模板

3次阅读

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

SQL pg_stat_activity 的 wait_event_type 与阻塞诊断模板

wait_event_type 时为什么总看到 ClientIO 却没发现锁?

因为 wait_event_type 描述的是「当前正在等什么」,不是「谁在阻塞你」。它只反映 postgresql 后端进程的即时等待状态,和锁链无关——比如 Client 表示在等客户端发下一条命令,IO 可能只是刷 WAL 或读数据页,这些都不是阻塞源。

真正要定位阻塞,得结合 pg_blocking_pids(pid) 或自连 pg_stat_activityblocking_pid 字段(14+ 版本);老版本只能靠 pg_locks 关联 granted = falsetransactionid 倒推。

  • wait_event_type 是“症状”,不是“病因”;别把它当锁表依据
  • 常见误判:Lock 类型确实可疑,但 Lock + wait_event = 'relation' 才大概率是 DDL 阻塞,而 Lock + 'transactionid' 多是长事务未提交
  • 如果 wait_event_typeClient,先看 state = 'idle in transaction' —— 这才是隐藏杀手,它不等资源,但会一直占着锁

pg_stat_activity 搭配 pg_locks 写阻塞诊断 SQL 时字段怎么对齐?

核心是把 pg_stat_activity.pidpg_locks.pid 关联,但要注意:10+ 版本 pg_locks.pid 是持有锁或等待锁的 backend pid,而 pg_stat_activitypid 是唯一标识,可直接 join;9.6 及更早需用 pg_locks.virtualxidtransactionid 间接关联,逻辑更绕。

一个稳妥的诊断模板(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_locksgranted = false 的行,并找对应 virtualxid 的最早持有者

wait_event_type = 'Lock'wait_event 是空值,怎么回事?

这是 PostgreSQL 14 引入的行为:当等待类型为 Lock,但具体锁对象尚未确定(比如正处在锁升级、或锁管理器内部路径中),wait_event 就留空。它不代表没锁,反而说明锁竞争发生在更底层,比如 tuple 级锁升级为 page 锁时的中间态。

  • 遇到空 wait_eventwait_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;,再人工比对 databaserelationtransactionid 字段,找「有锁没被授」的源头
  • 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 底层字段一层层剥。

text=ZqhQzanResources