# 关联日志、指标与计划

LLMS 索引： [llms.txt](/llms.txt)

---

activity 告诉你“此刻”，查询累计量告诉你“这段 epoch”，日志保存离散事件，指标保存时间序列，计划解释 executor 怎样处理数据。四者只有共享时间、实例、会话和查询身份，才能形成证据链。

## 8.3.1 日志最小基线与慢语句记录 {#item-8-3-1}

诊断日志的第一个目标不是“把所有 SQL 打出来”，而是让一条事件可定位：

```text
timestamp + timezone
cluster/instance
PID/session identity
user/database/application/client
severity/SQLSTATE
query or query identity
duration
transaction/session context
```

PostgreSQL 的 `log_line_prefix` 可以提供这些身份。一个需结合环境评估的示意：

```conf
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、磁盘上限、采集延迟、丢弃策略与敏感字段。

记录慢完成语句的核心参数是：

```conf
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 指标、主机资源与部署事件 {#item-8-3-2}

慢查询证据通常跨四层：

| 层 | 代表信号 | 用途 |
|---|---|---|
| 请求/应用 | 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 | 解释系统资源与邻居效应 |

再叠加一条“变化流”：

```text
application release
schema/index/statistics change
PostgreSQL/Pigsty/configuration change
failover/restart
data load/backfill/maintenance
traffic or tenant mix change
```

正确关联不是看到两条曲线同时升高，而是提出机制：

```text
发布改变 SQL predicate
  → query family calls 与 rows/call 上升
  → plan estimate/actual 偏离
  → shared blocks 与 CPU 同范围上升
  → endpoint p99 上升
  → 回滚 SQL shape 后上述指标按预期恢复
```

反例也同样重要：

- 主机 CPU 高，但目标 query 在 `Lock` wait：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 章的机器可读格式：

```sql
EXPLAIN (
    ANALYZE,
    BUFFERS,
    WAL,
    SETTINGS,
    SUMMARY,
    FORMAT JSON
)
SELECT ...;
```

它只应在安全、代表性环境执行。`ANALYZE` 会真实运行 SQL；写语句即使包在 rollback 中也可能有 sequence、外部函数等不可回滚副作用。事故时已有一条正在阻塞的生产写语句，不应为了“看计划”再执行一遍。

## 8.3.3 用同一时间轴排除巧合 {#item-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 |

这条链同时满足：

1. **先后**：候选原因发生在结果之前；
2. **同范围**：实例、database、user、application/query 能对应；
3. **剂量/机制**：blocker 存在时等待积累，释放后 waiter 前进；
4. **负对照**：未受影响路由或无 blocker 窗口不呈现同样现象；
5. **可逆性**：受控动作后结果按预测变化。

时间对齐时记录数据本身的分辨率：

- Prometheus scrape interval 与 recording rule window；
- Grafana 查询 step、rate window、timezone；
- 日志采集/索引延迟；
- trace sampling rate；
- PostgreSQL 累计统计刷新延迟与事务内 snapshot；
- 应用与数据库主机的 clock synchronization；
- failover 后 instance identity/计数器是否改变。

不要把面板像素对齐当毫秒级因果。某条一分钟 rate 曲线的点代表一个区间，日志 timestamp 代表事件，activity 是一次采样，它们的语义不同。先把每项转换成“事件或区间 + 误差/分辨率”，再讨论顺序。

一个实用的反巧合问题集：

```text
候选原因是否先于症状？
是否只出现在受影响范围？
它通过什么数据库机制产生该症状？
该机制应留下什么额外证据？
未受影响对照是否缺少这些证据？
只移除这一原因后，哪些指标应先后恢复？
若未恢复，这个假设怎样被判错？
```

输出时保留原始 artifact，而非只有解释。原始 CSV/JSON plan、日志行、Prometheus expression、时间范围、source hash 和查询参数分桶使其他人能够重算；截图最多是导航附件。

下一节将这些关联结果整理为一个有优先级、可反驳的假设树。

---

[上一节：从会话到语句定位范围](../02/) · [返回本章目录](../) · [下一节：建立而不是猜测假设](../04/) ·
[查看全书目录](/toc/) · [查看索引中心](/indexes/)
