跳至内容

7.2 正确使用 EXPLAIN

EXPLAIN 是观测工具,也会改变观测成本。普通 EXPLAIN 只规划;加 ANALYZE 后真实执行;加 timing、buffers、WAL 后又增加不同程度的采集工作。先明确问题,再选择选项。

7.2.1 EXPLAINANALYZEBUFFERSWAL

推荐的机器证据:

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

各选项回答:

  • ANALYZE:真实 rows、loops、time,且真的执行;
  • BUFFERS:shared/local/temp hit/read/dirtied/written;
  • WAL:records、FPI、bytes,主要对写路径有意义;
  • SETTINGS:影响 planner 且偏离 built-in default 的设置;
  • SUMMARY:planning/execution summary;
  • JSON/YAML/XML:给程序解析,文本留给人读。

buffer hit 不是“没有 I/O”,只表示 page 已在 PostgreSQL shared buffers;它可能刚由另一 backend 或操作系统读入。单次 warm run 不能代表 cold cache。WAL bytes 也不等于磁盘最终写入 bytes,FPI、compression、并发与 checkpoint 都会影响。

若只验证估算与树形,可先 EXPLAIN (FORMAT JSON),避免执行高风险/高成本 SQL;若要 actual,先用生产等价的只读副本、L1 或受控参数范围。不要在事故高峰对未知查询直接加 ANALYZE。

7.2.2 规划时间、执行时间与客户端时间

EXPLAIN 的 Planning Time 与 Execution Time 都是服务器视角,通常不包含:

  • 连接建立、pool queue;
  • 客户端序列化/反序列化;
  • 网络传输与 result consumption;
  • application thread/event-loop 排队;
  • transaction 中前后其他 SQL;
  • 在开始采集前已经发生的重试。

而 planner cost 连服务器毫秒也不是。诊断至少对齐四个时间:

application span
  = pool/connect + server round trip + decode + application work

server statement duration
  = parse/plan(可能缓存)+ lock/wait + executor + output

EXPLAIN planning/execution
  = 本次受 instrument 影响的服务器测量

dashboard sample
  = 采样时间窗中的聚合/近似

TIMING off 仍保留 actual rows/loops 与总 execution time,可降低逐节点读时钟开销;当目标是 cardinality 而非每节点时间时更合适。反复执行要记录次数、warmup、参数和并发,报告分布而不是最佳一次。

7.2.3 对写语句使用 ANALYZE 的事务保护

EXPLAIN ANALYZE UPDATE/DELETE/INSERT/MERGE 会真实改数据、触发 trigger、约束、WAL 与锁。最小演练模式:

BEGIN;
SET LOCAL statement_timeout = '30s';
SET LOCAL lock_timeout = '5s';

EXPLAIN (ANALYZE, BUFFERS, WAL, SETTINGS, FORMAT JSON)
UPDATE ...;

ROLLBACK;

rollback 能恢复同一 PostgreSQL transaction 内的数据变化,却不能撤销 sequence 值、某些外部副作用、通知接收方已经看到的消息,或 volatile function 对外部系统的动作。trigger/function 在演练环境也要审查。DDL、VACUUM 与不能在 transaction block 运行的命令又有不同边界。

生产写计划优先从同分布 L1、脱敏 clone 或 read-only EXPLAIN 开始;确需在线 ANALYZE 时限定精确 key、窗口、owner、timeout、before/after fingerprint,并确认复制/WAL预算。不要用 ROLLBACK 三个字把 R2/R3 动作伪装成 R0。

本章所有 ANALYZE 都是专属 fixture 上的 SELECT。task 保存原始 JSON,再由 Python 读取语义字段;它不 grep 文本节点,也不固定动态 cost/time。若 JSON 无效或 actual 行数漂移,分析立即失败。


上一节:优化器如何选择路径 · 返回本章目录 · 下一节:统计信息与估算偏差 · 查看全书目录 · 查看索引中心

最后更新于