PostgreSQL码农集散地

PG 19 破局可观测短板, 无损植入锁等待增量统计信息

本期播客

PG 19 又补上了一块可观测短板, 终于有锁等待增量统计信息了

数据库变慢,不一定是 SQL 写烂了,也不一定是 I/O 扛不住。
很多时候,真正把系统拖死的,是你看不见、却天天发生的锁等待。

PostgreSQL 在这次提交里,补上了一块长期存在的监控盲区:commit 4019f725。新加入的 pg_stat_lock,价值不在于“又多了一个视图”,而在于它第一次把锁行为沉淀成了可累计、可重置、可对比的统计事实。

过去不是看不到锁,而是只能“看现场”,很难“看趋势”

以前 PostgreSQL 当然能查锁。
pg_locks 能看“谁在等谁”,pg_stat_activity 能看“当前会话在等什么”,log_lock_waits 能把超过 deadlock_timeout 的长等待打进日志。

问题在于,这三者都更像事故现场,不像体检报告。

你能看到一次阻塞,不代表你知道过去 24 小时里,到底是 relation 锁、transactionid 锁,还是 advisory 锁,持续在拖慢业务。
这就是很多团队长期陷入“数据库玄学”的根源:有现象,没有累计事实;有告警,没有趋势画像。

pg_stat_lock最聪明的地方,不是“多”,而是“克制”

这次新增的不是按表、按 SQL、按会话的明细追踪,而是cluster 级、按 lock type 聚合的统计。
它只有 5 列:

  • locktype
  • waits
  • wait_time
  • fastpath_exceeded
  • stats_reset

很多人会嫌少,但恰恰因为少,这个设计才高级。

第一性原理很简单:
监控必须服务系统,不能反噬系统。

锁管理本来就是数据库最敏感的核心路径之一。如果你为了“看得更细”而把统计做成高基数、强实时、重明细,代价很可能直接打在并发控制路径上,最后变成“为了监控锁,先把锁搞慢”。
PostgreSQL 这次没有走这条歪路,而是选择了固定大小、按 LockTagType 聚合的实现。这不是保守,这是工程克制。

最关键的认知:waits 和 wait_time 不是“所有等待都算”

这是很多人最容易误解的地方。

pg_stat_lock.waits 和 wait_time 只在两个条件同时满足时才会累计:

  1. 这次加锁最终成功了
  2. 等待时间超过了 deadlock_timeout

这意味着什么?

这意味着 pg_stat_lock 不是纳秒级示波器,也不是全量事件追踪器。
它天然会漏掉那些短而频繁的微等待,所以它不适合替代全链路性能剖析工具。但反过来说,它特别适合回答一个更有管理价值的问题:

到底是哪一类锁,在持续制造足以影响业务体验的等待?

这不是一个“抓瞬时尖峰”的工具,而是一个“做长期治理”的仪表盘。

还有一个很容易被低估的指标:fastpath_exceeded

这个字段很有杀伤力。

它统计的是:某类锁原本想走 fast-path 快速路径,但因为 fast-path 槽位不够,最终没走成的次数。
官方回归测试专门构造了一个非常现实的场景:创建一个分区表,并把分区数做到超过 max_locks_per_transaction,然后访问这个分区表,结果 relation 类型的 fastpath_exceeded 会增长。

这说明什么?

说明在重度分区、宽事务、复杂查询的系统里,你以前觉得“最近锁开销怪怪的”,现在终于能把怀疑变成证据:
是不是 fast-path 容量打穿了?是不是锁获取开始更多回落到主锁表路径了?

这类问题过去不是不存在,而是缺少一个便宜、持续、内建的观测信号。

对 DBA、架构师、开发者,各自意味着什么

对 DBA 和架构师,这不是一个排查单次事故的小玩具,而是建立“锁画像”的基础设施。
你可以按 reset 周期持续观察:

  • relation 锁的 waits / wait_time 有没有抬头
  • transactionid 锁是不是在热点更新场景里持续上升
  • advisory 锁是不是已经被应用层滥用

对应用开发者,这个视图的意义同样直接:
它逼着你面对并发模型,而不是继续把一切甩锅给“数据库性能”。

如果 transactionid 等待长期偏高,问题通常不是 DBA 再加一个索引就能解决,而是你的事务边界、热点更新策略、重试机制、批处理方式出了问题。
说得更尖锐一点:

很多所谓“数据库慢”,本质上是应用并发设计粗糙。

但别神化它,它解决的是“长期缺位”,不是“一步到位”

pg_stat_lock 很有价值,但它不告诉你:

  • 具体是哪张表
  • 具体是哪条 SQL
  • 具体是哪个会话

所以它不能替代 pg_locks、pg_blocking_pids()、pg_stat_activity、SQL 日志和应用 trace。
正确姿势从来不是二选一,而是分层使用:

  • pg_stat_lock 看趋势
  • pg_locks 和 pg_stat_activity 看现场
  • 日志和 trace 补上下文

这才是完整的锁诊断闭环。

真正值得重视的结论

如果你的系统并发很低、锁冲突极少、schema 也不复杂,那 pg_stat_lock 的边际收益确实有限。
但只要你进入下面这些场景,它就很可能从“锦上添花”变成“运维必需品”:

  • 高并发 OLTP
  • 热点行更新
  • 重度分区表
  • 频繁 DDL
  • 大量 advisory lock
  • 宽事务、长事务并存

所以,这次提交不是一个“酷炫新功能”,而是 PostgreSQL 监控哲学一次非常成熟的补洞:
用足够便宜的方式,回答过去一直缺失、但生产系统又反复会问的问题。

真正优秀的数据库观测,不是把一切都采下来。
而是把最该采的、最能指导行动的信号留下来。
pg_stat_lock,就是这种克制而有效的工程产物。

你怎么看?
你更期待 PostgreSQL 下一步把锁统计继续细化到 relation 级,还是认为现在这种“低开销、低基数、强实用”的设计才是正路?

参考:
PostgreSQL 官方 commit
DeepWiki: postgres/postgres

附注:文中技术表述已结合官方 commit 补丁事实与 DeepWiki 的 PostgreSQL 知识做过一致性校验。