PostgreSQL码农集散地

pg_wait_tracer: 用 eBPF 搞定 PG 实时等待事件诊断

pg_wait_tracer: 用 BPF 硬件 watchpoint 把 PostgreSQL 等待事件变成“实时诊断电影”

https://github.com/DmitryNFomin/pg_wait_tracer

PostgreSQL 出问题时,DBA 最怕的不是“慢”,而是慢得没有证据。

CPU 高了,业务说数据库慢;IO 抖了,应用说查询卡;锁等待堆起来,开发说只是普通 UPDATE;连接数很多,运维说机器还有资源。最后大家围着几个快照指标争论:到底是 SQL 慢、锁慢、IO 慢、客户端慢,还是 PostgreSQL 内部争用慢?

pg_wait_tracer 想解决的不是“再多一个监控图”,而是一个更底层的问题:

PostgreSQL 的等待事件本来就记录了 backend 正在等什么,但传统方式很难把“每一次 wait event 状态切换”完整、实时、低侵入地抓下来。

它的思路很硬核:不用 PostgreSQL patch,不装 extension,不重启数据库,而是通过 BPF + CPU 硬件 debug register/watchpoint,盯住 PostgreSQL backend 里的 PGPROC->wait_event_info 字段。每当 backend 进入等待、退出等待、切换等待事件时,watchpoint 触发,BPF 程序记录这次状态变化。

这就把 PostgreSQL 的 wait event 从“当前快照”推进到了“全量时间线”。

一、背景:为什么 wait event 是 PostgreSQL 排障的核心入口?

数据库性能分析,本质上是在回答一句话:

时间花到哪里去了?

对 PostgreSQL 来说,一个 backend 的时间大致可以分成几类:

  • CPU 执行时间。
  • 存储 IO 等待。
  • WAL、buffer、proc array 等 LWLock 等待。
  • 行锁、事务锁、表锁、 advisory lock 等 Lock 等待。
  • ClientRead、ClientWrite 等客户端等待。
  • parallel query、replication 等 IPC 等待。
  • pg_sleep、timeout 等 Timeout 等待。
  • idle activity。

传统 DBA 很熟悉 pg_stat_activity.wait_event_type 和 wait_event。它能告诉你“这个 backend 此刻正在等什么”。

但问题在于:pg_stat_activity 是快照,不是录像。

如果一个事件只持续几百微秒、几毫秒,但每秒发生几万次,普通采样很可能看不到。即使看到了,也很难回答:

  • 这个等待累计消耗了多少 DB Time?
  • 平均等待多久,最大等待多久?
  • 等待延迟分布是什么样?
  • 哪个 session 贡献最多?
  • 哪个 query_id 贡献最多?
  • 等待事件之间有没有典型转移路径?
  • 这 5 秒、1 分钟、5 分钟的等待画像是否正在变化?

这就是 pg_wait_tracer 的切入点。

二、痛点:传统等待事件分析经常停在“快照猜测”

1. pg_stat_activity 只能看当前,不适合分析短等待

比如大量 IO:DataFileRead 每次只等几十微秒,但 QPS 很高、buffer miss 频繁。你用一秒一次的 SQL 采样,很可能看到的是一些随机瞬间。

如果等待很短但频率极高,它对整体 DB Time 的贡献可能很大;但快照方式很难准确累计。

2. pg_stat_statements 知道 SQL 慢,不知道慢在哪类等待

pg_stat_statements 很适合看 query 层面的总耗时、调用次数、平均耗时、shared/local/temp blocks 等指标。

但当一个 SQL 慢时,你还需要进一步判断:

  • 是 CPU-bound?
  • 是 IO-bound?
  • 是锁等待?
  • 是 WAL/LWLock 争用?
  • 是客户端消费结果慢?
  • 是并行查询 worker 同步问题?

pg_stat_statements 不是 wait event 归因工具。

3. pg_wait_sampling 类方案是采样,不是完整 transition trace

采样有价值,尤其适合低开销常驻监控。但采样天然有盲点:

  • 短事件容易漏。
  • 事件顺序不完整。
  • 很难做精确 latency histogram。
  • 很难重建 wait event transition。
  • 对 burst、thundering herd、短时间锁链定位不够细。

pg_wait_tracer 的卖点是 no sampling:捕获每一次 wait event transition。

4. 操作系统层 eBPF 工具不知道 PostgreSQL wait event 语义

perf、bcc、bpftrace、off-CPU 分析能告诉你进程在哪些内核栈、系统调用、调度点上消耗时间。

但 PostgreSQL wait event 是数据库内部语义:

  • LWLock:WALInsert
  • Lock:Transaction
  • IO:DataFileRead
  • Client:ClientRead
  • BufferPin

OS 工具看到的是系统层现象,DBA 最终还要映射回 PostgreSQL 语义。

5. 故障常常已经过去,现场没了

线上最常见的对话是:

“刚才 14:03 到 14:05 慢了一下,现在好了,你看下原因。”

如果没有持续记录,事后只能看日志、慢 SQL、系统指标、业务监控曲线。等待事件的细节往往已经丢了。

pg_wait_tracer 的 trace recording 和 offline replay,就是为这类事后诊断准备的。

三、传统方案:能定位一部分问题,但证据链经常不完整

目标
传统方案
不足
看当前等待
pg_stat_activity
快照视角,短等待易漏
看 SQL 总耗时
pg_stat_statements
缺少 wait event 归因
看系统 IO/CPU
iostat
、vmstat、perf、eBPF
缺少 PostgreSQL wait event 语义
看锁
pg_locks
、pg_blocking_pids()
更偏当前锁图,不是历史等待时间线
看长期趋势
Prometheus/Grafana
多为采样聚合,细粒度 transition 不完整
看事后现场
日志、慢 SQL、指标平台
wait event 事件流通常缺失
看等待分布
自建采样或扩展
难做到每次 transition 的纳秒级记录

所以传统方法不是没用,而是更像“拼图”:

  • PostgreSQL 内部视图给你当前现场。
  • pg_stat_statements 给你 SQL 维度累计。
  • OS 工具给你系统维度。
  • 日志给你部分历史事件。
  • 监控平台给你趋势。

pg_wait_tracer 的目标,是把 wait event 的时间线补上。

四、pg_wait_tracer 的方案:盯住 PGPROC->wait_event_info

README 对它的核心机制说得很清楚:

pg_wait_tracer 使用 CPU hardware debug register,也就是硬件 watchpoint,捕获每次对 PGPROC->wait_event_info 的写入。

PostgreSQL backend 在 wait event 状态变化时会更新这个字段。watchpoint 触发后,BPF 程序在 kernel context 中执行:

  1. 读取旧 wait event 和新 wait event。
  2. 用 bpf_ktime_get_ns() 计算前一个状态持续时间。
  3. 把 timestamp、pid、old_event、new_event、duration、query_id 发到 BPF ring buffer。
  4. 更新每个 backend 的状态,等待下一次 transition。

用户态程序消费 ring buffer,聚合成不同诊断视图;如果指定 --trace-dir,还会把原始事件写成 columnar + LZ4 压缩 trace 文件。

这个设计带来几个关键结果:

  • 不需要 PostgreSQL patch。
  • 不需要安装 PostgreSQL extension。
  • 不需要重启数据库。
  • 可以 attach 到正在运行的 PostgreSQL cluster。
  • 捕获的是每一次 wait event transition,而不是采样。
  • 可以实时分析,也可以落盘后离线 replay。

一句话:

它不是从 SQL 视图里采样 wait_event,而是在 PostgreSQL 更新 wait_event_info 的那一刻把事件拦下来。

五、核心能力拆解

1. 7 类诊断视图:从全局到 SQL、session、分布、转移

README 中列出的 7 个诊断视图是:

  • time_model
  • system_event
  • session_event
  • histogram
  • query_event
  • active
  • transitions

这套视图基本覆盖了 DBA 排障时的几个问题:

  • 总时间花在哪里?看 time_model。
  • 哪些等待事件最重?看 system_event。
  • 哪些 session 最重?看 session_event。
  • 单个事件延迟分布怎样?看 histogram。
  • 哪些 query_id 造成了等待?看 query_event。
  • 当前谁在等?看 active。
  • 等待事件之间如何转移?看 transitions。

2. time_model:DB Time 视角的总入口

time_model 是默认视图。它把 backend 的非 idle 时间拆成:

  • CPU*
  • IO
  • LWLock
  • Lock
  • BufferPin
  • Client
  • IPC
  • Timeout
  • Extension

其中 Activity/Idle 被排除在 DB Time 外。

典型使用:

sudo ./pg_wait_tracer --view time_model --count 1 --interval 5  

它回答的是最重要的第一问:

当前数据库时间主要花在 CPU、IO、锁、LWLock,还是客户端等待?

README 特别提醒:CPU* 表示 wait_event_info = 0,大多数情况下是 CPU 执行,但也可能包含 PostgreSQL 没有 instrument 的路径,所以带星号。

这点很重要,不要把 CPU* 机械理解成操作系统精确 CPU 时间。

3. system_event:找 Top wait event

system_event 按等待事件聚合,显示:

  • Total Waits
  • Total ms
  • Avg us
  • Max us
  • % DB

使用:

sudo ./pg_wait_tracer --view system_event --count 3  

判断方式很直接:

  • Total(ms) 高:这个事件是主要时间消耗。
  • Avg(us) 高但次数少:少量长等待,常见于锁、慢 IO。
  • 次数高但平均低:高频短等待,常见于 LWLock 或微秒级 IO transition。
  • Max 远高于平均值:存在尖刺或长尾,需要看 histogram。

4. session_event:按 backend 看 CPU/Wait 比例

session_event 展示每个 backend 的 DB Time、CPU%、Wait%、Top Wait。

使用:

sudo ./pg_wait_tracer --view session_event  

钻取单个 PID:

sudo ./pg_wait_tracer --view session_event --pid-filter 12345  

这适合回答:

  • 哪个 backend 最忙?
  • 某个 backend 是 CPU-bound 还是 wait-bound?
  • 某个 session 的主要等待是什么?
  • 系统进程如 checkpointer、walwriter、bgwriter 是否出现异常 IO 等待?

5. active:类似 top 的当前活动视图

active 显示当前 backend 状态:

  • on cpu
  • waiting
  • idle

以及当前 wait event、当前等待持续时间、累计 DB Time、backend type。

使用:

sudo ./pg_wait_tracer --view active  
sudo ./pg_wait_tracer --view active --sort db_time  

排序方式包括:

  • wait_time
  • db_time
  • pid
  • event

这个视图适合现场第一眼看:

  • 谁当前卡住最久?
  • 谁累计 DB Time 最高?
  • 当前等待是否集中在 Lock:Transaction、IO:DataFileRead、LWLock:WALInsert 等事件?

注意:active 依赖 live BPF state_map,replay 模式不可用。

6. histogram:看延迟分布,而不是只看平均值

平均值经常骗人。

一个 IO:DataFileRead 平均 100us,看起来不错,但如果 99% 是 10us,1% 是 10ms,用户体验可能已经有尖刺。

histogram 用 16 个 log2 bucket 展示单个事件的延迟分布:

sudo ./pg_wait_tracer --view histogram --event IO:DataFileRead  

可以判断:

  • 低 bucket 单峰:多数命中 OS page cache。
  • 双峰:一部分 cache,一部分真实磁盘 IO。
  • 右侧长尾:存储延迟尖刺。
  • 分布很散:可能有 noisy neighbor、I/O 抖动、调度问题。

7. query_event:把 wait event 归因到 query_id

query_event 需要 PostgreSQL 开启 compute_query_id = on 或 auto。

它有三种用法。

默认模式:看 Top query-event 组合:

sudo ./pg_wait_tracer --view query_event  

指定事件:看哪个 query 贡献了这个等待:

sudo ./pg_wait_tracer --view query_event --event IO:DataFileRead  

指定 query_id:看某个 query 的等待画像:

sudo ./pg_wait_tracer --view query_event --query-id 5678234567890123  

再用 pg_stat_statements 找 SQL 文本:

SELECTquery
FROM pg_stat_statements  
WHERE queryid = 5678234567890123;  

这个能力非常关键。因为调优不能只停在“IO 高”或“锁高”,最终要落到具体 SQL、具体业务路径。

8. transitions:从事件列表升级到等待状态转移图

Web client 中的 transitions 用 Sankey diagram 展示 wait event 状态转移,例如:

CPU -> IO:DataFileRead -> CPU  
CPU -> LWLock:WALInsert -> CPU  
CPU -> Lock:Transaction -> CPU  

这类视图适合看“行为模式”:

  • 某类 query 是 CPU/IO 交替?
  • 是否频繁从 CPU 转入 LWLock?
  • 锁等待之后是否继续进入 IO?
  • 某类等待组合是否代表一种执行计划或访问路径?

README 还提到 per-query fingerprint,例如:

IO:30%|CPU:22%|LWLock:21%|Lock:13%  

这类 fingerprint 对识别 query 画像变化很有价值。相同 query_id 如果等待画像突然变化,可能是数据分布、缓存状态、执行计划或并发形态变了。

六、三种运行模式:现场、常驻、回放

1. Interactive:现场诊断

默认模式 attach 到 PostgreSQL,实时展示视图。

sudo ./pg_wait_tracer --view system_event --count 3  

适合:

  • 临时排障。
  • 压测期间观察。
  • 故障正在发生时抓现行。

一次性采集 10 秒:

sudo ./pg_wait_tracer --count 1 --interval 10  

2. Daemon:常驻监控 + trace recording

daemon 模式会持续运行,并在 PostgreSQL 重启后自动 re-attach。

sudo ./pg_wait_tracer --daemon -T /var/lib/pgwt/traces -R 48  

其中:

  • -T 指定 trace 目录。
  • -R 48 保留 48 小时 trace 文件。

README 中说明 daemon 每个 interval tick 用 kill(postmaster_pid, 0) 检查 postmaster 是否存活。postmaster 死亡后,等待新实例出现,再从 PGDATA 的 postmaster.pid 找新 PID 并重新 attach。

注意:每次重新 attach 时 BPF 状态会重置。

3. Replay:事后分析

replay 模式读取已经完成的 trace 文件,不需要 root,也不需要 PostgreSQL 正在运行。

pg_wait_tracer --replay -T /var/lib/pgwt/traces --from 1h  

指定时间范围:

pg_wait_tracer --replay -T /var/lib/pgwt/traces \
    --from "2025-02-25T14:00:00" \
    --to "2025-02-25T14:30:00" \
    --view system_event  

限制要记住:

  • active 视图 replay 不可用。
  • current.trace 正在写入,没有 footer,不能被 replay 和 pgwt-server 读取。
  • replay 只能读取已经 rotate 完成的 .trace.lz4 文件。
  • replay 输出格式固定为 text。

七、Web investigation client:把 trace 文件变成浏览器调查工作台

pgwt 是浏览器调查客户端。它从你的笔记本发起,通过 SSH 连接数据库服务器,不需要数据库账号。

使用:

pgwt root@db-server  

指定 trace 目录和远端 server binary:

pgwt --trace-dir /var/lib/pgwt/traces \
     --server-path /usr/local/bin/pgwt-server \
     root@db-server  

架构是:

[Your laptop]                        [DB server]  
pgwt (Go binary)                     pgwt-server (C binary)  
  ├─ spawns: ssh user@host             ├─ reads trace files  
  │    pgwt-server <trace-dir>         ├─ computes aggregates  
  ├─ localhost:8384 HTTP server        └─ JSON lines on stdin/stdout  
  └─ browser UI (ECharts)  

这个设计很适合生产环境:

  • trace 文件留在 DB server。
  • 本地只跑 UI。
  • 不需要开放数据库连接。
  • 不需要数据库凭证。
  • 通过 SSH 复用现有运维通道。

Web UI 重点能力包括:

  • AAS stacked area chart。
  • 11 类 wait class 颜色。
  • drag-to-select 缩放。
  • Overview、Events、Sessions、Queries 表格。
  • 点击 wait class/event/session/query 逐层 drill-down。
  • breadcrumb 回退。
  • percentage bars。
  • summary metrics。
  • sortable columns。
  • WebSocket 自动重连。
  • concurrency peak overlay。
  • burst detection markers。
  • Sankey transitions。
  • lock chains。
  • interference scoring。

这已经不是一个简单 CLI 工具,而是一个 PostgreSQL 等待事件调查台。

八、Trace 文件:为“故障已过去”准备证据

指定 --trace-dir 后,事件会写入 columnar、LZ4 压缩文件。

README 描述的文件结构包括:

  • 28 字节 file header。
  • 多个 block。
  • 每个 block 有 first/last timestamp、event 数量、压缩/未压缩大小。
  • 列包括 timestamp、pid、old_event、new_event、duration、query_id。
  • footer 里有 block index。

生命周期:

  • 正在写的文件叫 current.trace。
  • 每个自然小时 rotate。
  • rotate 后命名为 YYYY-MM-DD_HH.trace.lz4。
  • retention 按小时删除旧文件。
  • 每个 block 4096 events。
  • README 给出的典型压缩率约 36x。

这套文件格式的价值是:

  • 可以长期保存短期高精度 wait event 证据。
  • 可以事后 replay。
  • 可以给 Web UI 快速聚合。
  • 可以做跨时间窗口对比。

九、效果对比:pg_wait_tracer 改变的是“时间归因精度”

维度
传统方式
pg_wait_tracer
部署方式
SQL 视图、扩展、日志、外部监控组合
不 patch、不装 extension、不重启,root attach
采集方式
快照或采样为主
捕获每一次 wait event transition
时间精度
取决于采样周期
README 声称纳秒级 duration
当前现场
pg_stat_activityactive
 + BPF state
全局时间模型
需要自己聚合多指标
time_model
事件排名
采样或外部聚合
system_event
session 归因
需要快照/采样
session_event
query 归因
pg_stat_statements
 只能看 SQL 总耗时
query_event
 按 query_id 关联 wait event
延迟分布
常规视图不直接提供
histogram
事件转移
基本缺失
Sankey transitions
事后复盘
依赖日志和监控曲线
trace replay
开销
通常较低,取决于方案
与 transition rate 成正比,README 给出 6% 到 29% 场景

一句话:

传统监控经常告诉你“系统慢了”;pg_wait_tracer 更接近告诉你“每个 backend 的时间被哪些 wait event 切走了”。

十、性能与开销:它不是免费的

这是使用 pg_wait_tracer 必须认真看的部分。

README 给出的 benchmark 环境:

  • Hetzner cx43
  • 8 vCPU
  • 16 GB RAM
  • Rocky 9.7
  • PostgreSQL 18
  • pgbench scale 100
  • 8 clients
  • 60 秒运行
  • 5 次重复

写多 OLTP 场景:

  • baseline 约 5000 TPS。
  • transition rate 约 40K/s。
  • full trace 开销约 6%。
  • --skip-query-id 约 5%。
  • --lightweight 约 4%。

读多且高 buffer miss 场景:

  • baseline 约 108000 TPS。
  • transition rate 约 220K/s。
  • full trace 开销约 29%。
  • --skip-query-id 约 26%。
  • --lightweight 约 28%。

这个结果说明一个非常关键的原则:

pg_wait_tracer 的开销主要不取决于 SQL 有多复杂,而取决于 wait event transition 有多频繁。

README 把开销来源拆成:

  • 约 70% 来自 hardware debug exception。
  • 约 25% 来自 lock amplification。
  • 约 5% 来自 BPF + userspace。

这意味着 --lightweight 和 --skip-query-id 只能省一小部分。真正的成本在硬件 debug exception 本身。

使用建议

推荐这样定位:

  • 临时排障:可以开 full trace。
  • 压测诊断:可以开 full trace,并记录 overhead。
  • 常驻监控:优先考虑 daemon + trace retention,但要在本业务负载下压测。
  • 高 TPS、读多、shared_buffers 小、buffer miss 高的场景:谨慎常驻 full trace。
  • 只需要 time_model 和 system_event:可考虑 --lightweight。
  • 不需要 query 归因:可考虑 --skip-query-id。

不要把 README 中的 6% 或 29% 机械套到自己的系统。你应该在自己的 workload 上测 transition rate 和 TPS 变化。

十一、场景使用实践

场景 1:线上突然慢,先判断 DB Time 花在哪里

执行:

sudo ./pg_wait_tracer --count 1 --interval 10  

或明确使用:

sudo ./pg_wait_tracer --view time_model --count 1 --interval 10  

判断:

  • CPU* 高:可能是 CPU-bound,也可能有未 instrument 的路径。
  • IO 高:继续看 system_event 中具体 IO 事件。
  • Lock 高:继续看 Lock:Transaction、Lock:Relation、Lock:Tuple。
  • LWLock 高:看是否 WALInsert、BufferContent、LockManager。
  • Client 高:检查客户端消费、网络、连接池。

场景 2:IO 高,确认是读、写、sync 还是 WAL

sudo ./pg_wait_tracer --view system_event --count 1 --interval 10  

如果 Top event 是:

  • IO:DataFileRead:看是否缺索引、扫描过大、shared_buffers 不足、缓存命中差。
  • IO:DataFileWrite:看 dirty page 写压力和 checkpoint。
  • IO:DataFileSync:看 fsync、存储延迟、checkpoint sync。
  • IO:WALWrite / IO:WALSync:看 WAL 设备、事务提交频率、同步提交策略。

再用 histogram 看分布:

sudo ./pg_wait_tracer --view histogram --event IO:DataFileRead  

如果右侧长尾明显,重点查存储延迟尖刺,而不是只看平均值。

场景 3:锁等待高,先找 session,再找 query

先看系统事件:

sudo ./pg_wait_tracer --view system_event --count 1 --interval 10  

如果 Lock:Transaction 高,看 session:

sudo ./pg_wait_tracer --view session_event  

现场看当前卡住的 backend:

sudo ./pg_wait_tracer --view active --sort wait_time  

再结合 PostgreSQL 内部锁视图查 blocker:

SELECT
    a.pid AS waiter_pid,  
    pg_blocking_pids(a.pid) AS blocker_pids,  
    a.wait_event_type,  
    a.wait_event,  
    a.query  
FROM pg_stat_activity a  
WHERE cardinality(pg_blocking_pids(a.pid)) > 0;  

Web client 里的 lock chains 可以进一步做 waiter -> blocker 推断,但这类推断应作为调查线索,最终仍建议结合数据库锁视图确认。

场景 4:某个 SQL 慢,判断它到底在等什么

前提:开启 compute_query_id。

compute_query_id = on  

看 query-event 组合:

sudo ./pg_wait_tracer --view query_event  

找 SQL 文本:

SELECT queryid, calls, mean_exec_time, query
FROM pg_stat_statements  
WHERE queryid = 5678234567890123;  

看指定 query 的等待画像:

sudo ./pg_wait_tracer --view query_event --query-id 5678234567890123  

如果它主要是:

  • IO:DataFileRead:优先看执行计划、索引、数据访问量。
  • Lock:Transaction:优先看并发写冲突和长事务。
  • LWLock:WALInsert:优先看高并发写 WAL 争用。
  • Client:ClientWrite:优先看应用端消费结果。

场景 5:压测时比较 5 秒、1 分钟、5 分钟趋势

sudo ./pg_wait_tracer --window 5s,1m,5m  

这个模式适合看趋势变化:

  • 最近 5 秒 IO 降了,但 5 分钟 IO 很高:系统可能刚恢复。
  • 最近 5 秒 Lock 飙升,但 1 分钟不高:可能是短时锁风暴。
  • 5 秒、1 分钟、5 分钟 LWLock 都高:可能是持续内部争用。

注意 README 要求:第一个 window 必须等于 interval,window 要递增。

场景 6:故障已过去,用 replay 复盘

前提是 daemon 已经持续记录 trace:

sudo ./pg_wait_tracer --daemon -T /var/lib/pgwt/traces -R 48  

事后看最近一小时:

pg_wait_tracer --replay -T /var/lib/pgwt/traces --from 1h  

看具体窗口:

pg_wait_tracer --replay -T /var/lib/pgwt/traces \
    --from "2025-02-25T14:00:00" \
    --to "2025-02-25T14:30:00" \
    --view query_event  

快速摘要:

pgwt-server --dump /var/lib/pgwt/traces  

这对“刚才慢了一下,现在好了”的问题非常有用。

十二、实操:从安装到第一次诊断

1. 环境要求

README 给出的要求:

  • Linux kernel >= 5.8。
  • PostgreSQL 17 或 18。
  • PostgreSQL 14-16 支持有限,需看 INSTALL。
  • root 权限,或 CAP_SYS_ADMIN + CAP_SYS_PTRACE。
  • x86_64 或 aarch64。

2. 编译

make  

具体依赖按项目的 INSTALL.md 准备。

3. 自动发现并 attach 单实例

sudo ./pg_wait_tracer  

如果系统只有一个 PostgreSQL 实例,会自动发现并 attach。
如果有多个实例,会列出并退出,需要指定 --pid 或 --pgdata。

指定 postmaster PID:

sudo ./pg_wait_tracer --pid 12345  

指定 PGDATA:

sudo ./pg_wait_tracer --pgdata /var/lib/pgsql/18/data  

4. 一次性采集

采 10 秒后退出:

sudo ./pg_wait_tracer --count 1 --interval 10  

采 3 个 interval:

sudo ./pg_wait_tracer --view system_event --count 3 --interval 5  

5. 多窗口对比

sudo ./pg_wait_tracer --window 5s,1m,5m  

指定视图:

sudo ./pg_wait_tracer --view system_event --window 5s,1m,5m  

6. daemon 常驻记录

创建 trace 目录时要考虑权限和磁盘空间。启动:

sudo ./pg_wait_tracer --daemon -T /var/lib/pgwt/traces -R 48  

如果需要让特定 Unix group 读取 trace:

sudo ./pg_wait_tracer --daemon \
    -T /var/lib/pgwt/traces \
    -R 48 \
    --trace-group dba  

7. 离线 replay

pg_wait_tracer --replay -T /var/lib/pgwt/traces --from 1h  

指定视图:

pg_wait_tracer --replay -T /var/lib/pgwt/traces \
    --from 2h \
    --view query_event  

8. 浏览器调查

从本地笔记本运行:

pgwt root@db-server  

自定义:

pgwt --trace-dir /var/lib/pgwt/traces \
     --server-path /usr/local/bin/pgwt-server \
     root@db-server  

9. 快速文本摘要

pgwt-server --dump /var/lib/pgwt/traces  

适合 SSH one-liner、值班快速查看、脚本化摘要。

十三、推荐落地路径

第一阶段:压测环境验证

先不要直接上生产常驻。建议在压测环境执行:

sudo ./pg_wait_tracer --count 1 --interval 30  

对比开启前后的:

  • TPS。
  • P95/P99 延迟。
  • CPU 使用率。
  • PostgreSQL wait profile。
  • transition rate。

如果是 read-heavy、高 buffer miss、高 TPS 场景,要特别关注 overhead。

第二阶段:生产临时排障

故障现场短时间运行:

sudo ./pg_wait_tracer --view time_model --count 1 --interval 10  
sudo ./pg_wait_tracer --view system_event --count 1 --interval 10  
sudo ./pg_wait_tracer --view active --sort wait_time  

这类用法风险相对可控,因为运行时间短、目标明确。

第三阶段:生产 daemon 小范围常驻

选择关键库或问题库,开启 trace retention:

sudo ./pg_wait_tracer --daemon -T /var/lib/pgwt/traces -R 24  

建议先保留 24 小时,观察:

  • trace 文件增长速度。
  • 磁盘占用。
  • PostgreSQL 性能变化。
  • replay 是否能覆盖常见事后排障窗口。

第四阶段:形成排障 SOP

建议把下面几条固化成值班手册:

  • 慢:先看 time_model。
  • IO 高:看 system_event + histogram。
  • 锁高:看 active + session_event + pg_blocking_pids()。
  • SQL 慢:看 query_event + pg_stat_statements。
  • 已恢复:用 replay 或 pgwt-server --dump。
  • 模式变化:用 multi-window 或 Web UI transitions/fingerprint。

十四、边界条件与风险

1. root/CAP 权限门槛

它需要 root,或 CAP_SYS_ADMIN + CAP_SYS_PTRACE。
这意味着它不是一个普通数据库账号能使用的工具,而是 DBA/SRE 级别的主机诊断工具。

2. overhead 与 transition rate 强相关

README 已经给出高 transition rate 场景下接近 29% 的 TPS overhead。
所以不要把它当成“无成本常驻 agent”。

正确做法是:

  • 先压测。
  • 再短时生产验证。
  • 最后决定是否常驻。

3. CPU* 不是精确 CPU profiler

CPU* 表示没有 PostgreSQL wait event set。它大部分是 CPU 执行,但也包括未 instrument 的路径。

如果要分析 CPU 函数热点,仍然需要 perf、火焰图等工具。

4. replay 不能读未 rotate 的 current.trace

如果故障发生在当前小时,current.trace 还没 footer,README 说明 C replay 和 pgwt-server 不能读它。
需要等 rotate 后分析,或者使用 live/Web live 能力时按项目实际支持情况处理。

5. query_event 依赖 query_id

如果没有开启 compute_query_id,或者无法稳定获取 query_id,query 归因价值会下降。

6. PG14-16 支持有限

README 明确 PostgreSQL 17/18 是主要支持版本,PG14-16 有限制,需要看 INSTALL.md。
生产落地前必须在目标版本验证。

十五、和 PostgreSQL 原生 wait event timing patch 的关系

README 最后提到一个未来方向:PostgreSQL-native wait event timing patch。

这个 patch 的目标是在 PostgreSQL 内部直接记录 wait event timing,避免硬件 watchpoint 的 debug exception 成本。README 给出的对比是:

方案
worst-case overhead
单事件成本
PostgreSQL patch
< 0.5%
约 70-100 ns
pg_wait_tracer hardware watchpoint
29%
约 200-300 ns

这个方向说明一件事:

pg_wait_tracer 当前的最大价值,是无需改 PostgreSQL 就能获得完整 wait event trace;但从长期看,最优路径可能是 PostgreSQL 内核原生支持 wait event timing。

所以对 DBA 和内核开发者来说,这两个方向不是互斥的:

  • 今天要诊断 stock PostgreSQL:pg_wait_tracer 很有价值。
  • 未来希望低开销常驻:原生 wait event timing 更理想。
  • 即使未来有原生统计,pgwt 这类调查客户端和 trace/replay 思路仍然有产品价值。

十六、结论

pg_wait_tracer 是一个很有攻击性的 PostgreSQL 诊断工具。

它的价值不在于“又展示了几个 wait event 表格”,而在于它抓的是每一次 wait event transition,并把这些 transition 组织成:

  • DB Time time model。
  • system event 排名。
  • session 归因。
  • query_id 归因。
  • latency histogram。
  • active 现场。
  • transitions Sankey。
  • lock chain 推断。
  • interference scoring。
  • trace recording。
  • offline replay。
  • Web drill-down 调查台。

它最适合的场景是:

  • 复杂性能问题现场诊断。
  • 压测期间等待画像分析。
  • 锁、IO、LWLock、Client 等等待归因。
  • 短时 burst 和并发干扰分析。
  • 故障事后复盘。
  • PostgreSQL 内核/扩展/运维团队做深度性能调查。

它不适合被无脑当成所有生产库的低成本常驻 agent。原因也很明确:硬件 watchpoint 的成本随 wait event transition rate 增长,高频短等待场景 overhead 可能明显。

最后一句话:

如果说 pg_stat_activity 是 PostgreSQL 等待事件的“照片”,pg_wait_tracer 就是在拍“电影”。照片适合看当前,电影适合复盘因果;但电影要占更多资源,所以要在真正需要看清过程的时候打开它。