8.3 关联日志、指标与计划
activity 告诉你“此刻”,查询累计量告诉你“这段 epoch”,日志保存离散事件,指标保存时间序列,计划解释 executor 怎样处理数据。四者只有共享时间、实例、会话和查询身份,才能形成证据链。
8.3.1 日志最小基线与慢语句记录
诊断日志的第一个目标不是“把所有 SQL 打出来”,而是让一条事件可定位:
timestamp + timezone
cluster/instance
PID/session identity
user/database/application/client
severity/SQLSTATE
query or query identity
duration
transaction/session contextPostgreSQL 的 log_line_prefix 可以提供这些身份。一个需结合环境评估的示意:
log_line_prefix = '%m [%p] %c %q%u@%d/%a '其中 %m 是带毫秒时间,%p 是 PID,%c 由 backend start time 与 PID 组成近似唯一的 session ID,%u/%d/%a 分别是用户、数据库和 application。若启用 compute_query_id,还可评估 %Q;但官方明确指出 log_statement 产生日志时 query ID 可能尚未计算,因此不能把某一类日志里的 %Q=0 当作真实 query identity。
生产日志更适合使用 csvlog 或 jsonlog 等结构化目标,由采集器保留字段,而不是依赖脆弱正则拆纯文本。无论格式如何,都要实测 rotation、磁盘上限、采集延迟、丢弃策略与敏感字段。
记录慢完成语句的核心参数是:
log_min_duration_statement = '...ms'它在语句完成且 duration 达阈值时记录,因此能发现“已结束的慢”,却不能解释当前一直没结束的 blocker。阈值不是通用常数:OLTP、批处理与维护任务的正常时长不同。先根据 SLO、流量和日志预算设基线,再用 role/database/session 或采样策略控制范围。
相关开关解决不同问题:
| 设置 | 能回答 | 主要代价/边界 |
|---|---|---|
log_min_duration_statement | 哪些完成语句越过阈值 | 高流量下日志量;不记录未完成 |
log_min_duration_sample + sample rate | 采样慢/常规语句 | 样本不等于全量;需保留采样率 |
log_lock_waits | 等锁超过 deadlock_timeout 的事件 | 阈值与日志量;不是所有短锁等待 |
log_temp_files | 超阈值临时文件 | 只证明 spill/临时文件,不自动证明根因 |
auto_explain | 被采样语句的执行计划 | ANALYZE/timing 成本、日志量与参数暴露 |
log_statement | 某类语句文本 | all 通常过量且可能泄密;不是性能万能开关 |
参数值比 SQL shape 更敏感。extended query protocol 的日志可能包含 bind parameters;log_parameter_max_length 和 log_parameter_max_length_on_error 可限制或禁止记录,但非零错误参数记录也会增加保存文本表示的开销。策略至少应覆盖:
- token、密码、密钥、个人数据和业务 payload 的禁止/脱敏;
- 每值长度和整条消息长度;
- 谁能读取、传输是否加密、保留多久;
- exporter/log pipeline 是否会复制到更多系统;
- 调查结束后如何恢复临时配置。
不要在事故中即兴全局打开 log_statement=all、auto_explain.log_analyze=on 和完整参数。先估算事件率、单条字节、磁盘余量与性能成本;能按单一 role/database/session 小范围复现时,就不要扩大到全局。
8.3.2 SQL 指标、主机资源与部署事件
慢查询证据通常跨四层:
| 层 | 代表信号 | 用途 |
|---|---|---|
| 请求/应用 | route latency、errors、retries、pool wait、in-flight | 确认用户影响与 PostgreSQL 外时间 |
| PostgreSQL query/session | calls/time/rows、wait、locks、plans、temp/WAL | 锁定查询族与数据库机制 |
| PostgreSQL instance | connections、xacts、checkpoints、WAL、vacuum、I/O | 判断共享资源与后台活动 |
| 主机/平台 | CPU、run queue、memory pressure、disk latency/queue、network | 解释系统资源与邻居效应 |
再叠加一条“变化流”:
application release
schema/index/statistics change
PostgreSQL/Pigsty/configuration change
failover/restart
data load/backfill/maintenance
traffic or tenant mix change正确关联不是看到两条曲线同时升高,而是提出机制:
发布改变 SQL predicate
→ query family calls 与 rows/call 上升
→ plan estimate/actual 偏离
→ shared blocks 与 CPU 同范围上升
→ endpoint p99 上升
→ 回滚 SQL shape 后上述指标按预期恢复反例也同样重要:
- 主机 CPU 高,但目标 query 在
Lockwait:CPU 可能是另一 workload; - shared block hit 高:只说明 PostgreSQL buffer hit,不代表没有 CPU 或 kernel page-cache I/O;
- disk latency 上升,但目标 plan buffers 几乎全 hit:时间相关不等于该 query 被磁盘拖慢;
- temp files 上升,但来自报表 user,而事故来自 OLTP application;
- 发布与事故同时发生,但未发布的 control instance 也同样退化:优先调查共享依赖。
PostgreSQL 的 pg_stat_io 和 pg_statio_* 能补充数据库 I/O 视角,但官方提醒它们不能区分数据是从物理介质还是 kernel page cache 取得;仍需与操作系统工具联合。track_io_timing 能提供时间,但有平台计时开销,是否常开需基准验证。
计划证据沿用第 7 章的机器可读格式:
EXPLAIN (
ANALYZE,
BUFFERS,
WAL,
SETTINGS,
SUMMARY,
FORMAT JSON
)
SELECT ...;它只应在安全、代表性环境执行。ANALYZE 会真实运行 SQL;写语句即使包在 rollback 中也可能有 sequence、外部函数等不可回滚副作用。事故时已有一条正在阻塞的生产写语句,不应为了“看计划”再执行一遍。
8.3.3 用同一时间轴排除巧合
把证据转换成 UTC 事件表,比叠十张截图更容易看出顺序:
| UTC | 事件 | 身份/范围 | 证据 |
|---|---|---|---|
| 12:03:42 | release 2026.07.29-3 开始 | app-a | deploy log |
| 12:04:01 | route p99 越过 SLO | checkout/create | histogram + count |
| 12:04:05 | pool wait 上升 | app-a | pool metric |
| 12:04:07 | Lock wait 出现 | db/shop, query X | activity snapshot |
| 12:04:07 | blocker edge X→Y | PID + backend_start | pg_blocking_pids() |
| 12:04:31 | 精确取消 Y | approved action | audit log |
| 12:04:32 | X 前进,pool queue 回落 | same scope | session/pool metrics |
| 12:04:45 | p99 恢复 | same route | histogram |
这条链同时满足:
- 先后:候选原因发生在结果之前;
- 同范围:实例、database、user、application/query 能对应;
- 剂量/机制:blocker 存在时等待积累,释放后 waiter 前进;
- 负对照:未受影响路由或无 blocker 窗口不呈现同样现象;
- 可逆性:受控动作后结果按预测变化。
时间对齐时记录数据本身的分辨率:
- Prometheus scrape interval 与 recording rule window;
- Grafana 查询 step、rate window、timezone;
- 日志采集/索引延迟;
- trace sampling rate;
- PostgreSQL 累计统计刷新延迟与事务内 snapshot;
- 应用与数据库主机的 clock synchronization;
- failover 后 instance identity/计数器是否改变。
不要把面板像素对齐当毫秒级因果。某条一分钟 rate 曲线的点代表一个区间,日志 timestamp 代表事件,activity 是一次采样,它们的语义不同。先把每项转换成“事件或区间 + 误差/分辨率”,再讨论顺序。
一个实用的反巧合问题集:
候选原因是否先于症状?
是否只出现在受影响范围?
它通过什么数据库机制产生该症状?
该机制应留下什么额外证据?
未受影响对照是否缺少这些证据?
只移除这一原因后,哪些指标应先后恢复?
若未恢复,这个假设怎样被判错?输出时保留原始 artifact,而非只有解释。原始 CSV/JSON plan、日志行、Prometheus expression、时间范围、source hash 和查询参数分桶使其他人能够重算;截图最多是导航附件。
下一节将这些关联结果整理为一个有优先级、可反驳的假设树。
上一节:从会话到语句定位范围 · 返回本章目录 · 下一节:建立而不是猜测假设 · 查看全书目录 · 查看索引中心