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:WALInsertLock:TransactionIO:DataFileReadClient:ClientReadBufferPin
OS 工具看到的是系统层现象,DBA 最终还要映射回 PostgreSQL 语义。
5. 故障常常已经过去,现场没了
线上最常见的对话是:
“刚才 14:03 到 14:05 慢了一下,现在好了,你看下原因。”
如果没有持续记录,事后只能看日志、慢 SQL、系统指标、业务监控曲线。等待事件的细节往往已经丢了。
pg_wait_tracer 的 trace recording 和 offline replay,就是为这类事后诊断准备的。
三、传统方案:能定位一部分问题,但证据链经常不完整
pg_stat_activity | ||
pg_stat_statements | ||
iostatvmstat、perf、eBPF | ||
pg_lockspg_blocking_pids() | ||
所以传统方法不是没用,而是更像“拼图”:
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 中执行:
读取旧 wait event 和新 wait event。 用 bpf_ktime_get_ns()计算前一个状态持续时间。把 timestamp、pid、old_event、new_event、duration、query_id 发到 BPF ring buffer。 更新每个 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_modelsystem_eventsession_eventhistogramquery_eventactivetransitions
这套视图基本覆盖了 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 cpuwaitingidle
以及当前 wait event、当前等待持续时间、累计 DB Time、backend type。
使用:
sudo ./pg_wait_tracer --view active
sudo ./pg_wait_tracer --view active --sort db_time
排序方式包括:
wait_timedb_timepidevent
这个视图适合现场第一眼看:
谁当前卡住最久? 谁累计 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_stat_activity | active | |
time_model | ||
system_event | ||
session_event | ||
pg_stat_statements | query_event | |
histogram | ||
一句话:
传统监控经常告诉你“系统慢了”;
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 给出的对比是:
这个方向说明一件事:
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就是在拍“电影”。照片适合看当前,电影适合复盘因果;但电影要占更多资源,所以要在真正需要看清过程的时候打开它。