一句话定义
EXPLAIN 让优化器在不真正执行(MySQL)或附带真实执行统计(PostgreSQL 的 ANALYZE)的情况下,输出它为 SQL 选定的执行计划——访问路径、连接顺序、估算行数,是慢查询诊断的第一工具。
为什么重要
- 性能问题的诊断顺序是「先看计划、再谈索引、最后才改代码」,跳过 EXPLAIN 直接加缓存是本末倒置。
- 执行计划把 kp-009/kp-011 的机制知识与 kp-013 的优化器行为连成闭环:你看到的每一行输出都有确定的机制解释。
- 它可完全离线复现:一个空库加一万行数据,就能演示全表扫描与索引扫描的性能悬崖。
前置知识
kp-004(连接与聚合)、kp-011(联合索引与覆盖索引)。
核心概念
- MySQL 关键列:
type(访问类型,从优到劣 system/const/eq_ref/ref/range/index/ALL)、key(实际用到的索引)、rows(估算扫描行数)、Extra(Using index / Using where / Using filesort / Using temporary)。 - PostgreSQL 关键输出:Seq Scan vs Index (Only) Scan、Rows(估算)、Actual Rows(真实)、cost=启动..总代价、Buffers(共享命中/读)。
EXPLAIN ANALYZE(PG)/EXPLAIN ANALYZE(MySQL 8.0.18+):真执行并回报实际行数与耗时,用于校准估算误差。- 优化提示(Hint):
FORCE INDEX、PG 的enable_seqscan=off等实验手段(用于验证,不用于长期方案)。
原理与机制
EXPLAIN 读数的三条判据:一看 type/Scan 方式——ALL/Seq Scan 表示全表扫描,可能因无索引、统计信息失真或选择性差(全表比走二级索引+回表更便宜);二看 rows 与实际行数的偏差——偏差一个数量级以上通常意味着统计信息过期(ANALYZE TABLE / PG ANALYZE 可修复);三看 Extra——Using filesort 表示排序内存/磁盘化,Using temporary 表示物化中间结果,二者都提示需要索引或改写。执行计划是树:MySQL 竖排输出,上层是驱动结果消费者;PG 缩进表示父子算子,最内层是数据源。慢查询治理流程固定为:慢日志抓取 → EXPLAIN ANALYZE 取证 → 定位最贵算子 → 索引/改写 → 复测对比。
实例或案例
可复现实验:以下脚本可在本地库直接执行。
-- 实验环境:MySQL 8 或 PostgreSQL 16 任选其一
CREATE TABLE t_order (
id BIGINT PRIMARY KEY,
user_id BIGINT NOT NULL,
status VARCHAR(16) NOT NULL,
created_at TIMESTAMP NOT NULL,
KEY idx_user_status_created (user_id, status, created_at)
);
-- 灌入 1,000,000 行(用存储过程或 generate_series,两库均可)
-- MySQL:写循环存储过程插入;PostgreSQL:
INSERT INTO t_order
SELECT g, g % 10000,
(ARRAY['created','paid','done'])[1 + g % 3],
now() - (g || ' minutes')::interval
FROM generate_series(1, 1000000) AS g;
ANALYZE t_order;
-- 实验 A:等值+排序,应命中 idx_user_status_created 且免 filesort
EXPLAIN ANALYZE
SELECT id FROM t_order
WHERE user_id = 42 AND status = 'paid'
ORDER BY created_at DESC LIMIT 20;
-- 实验 B:范围条件截断联合索引尾列,观察扫描行数放大
EXPLAIN ANALYZE
SELECT id FROM t_order
WHERE user_id = 42 AND status = 'paid' AND created_at > now() - interval '30 day'
ORDER BY created_at DESC;
预期读数:实验 A 为 ref 访问 + Using index(覆盖索引)、rows ≈ 数百、毫秒级;把 SELECT id 改成 SELECT * 后 Extra 变为需要回表、耗时上升——这就亲手复现了覆盖索引的价值。
常见误区
- 只看耗时下结论:同一 SQL 在缓存冷热不同时耗时波动巨大,计划与行数才是稳定证据。
- 用生产高峰直接跑
EXPLAIN ANALYZE:它会真执行,写操作与重查询要谨慎,先在从库或影子环境验证。 - 加了索引不
ANALYZE:统计信息不更新,优化器可能继续选旧计划,误判「索引没用」。
自测题
type=ALL一定是坏计划吗? 答:不一定。当条件选择性差(命中行占比高)或表极小时,全表扫描比二级索引+大量回表更便宜;要结合 rows 估算与表大小判断。- rows 估算严重失真怎么办? 答:执行
ANALYZE刷新统计信息;仍失真则检查是否有数据倾斜、绑定变量导致的参数嗅探问题,必要时用 Hint 验证并反馈给 DBA。
公式或模型
本节不适用:本知识点是工具与流程,代价模型在 kp-013 展开。
图示
本节不适用:执行计划本身就是结构化「树图」,原样阅读输出比转画图更贴近工程现场。
直观类比
EXPLAIN 像物流系统的路径预览:还没发车(执行)就告诉你走哪条高速(索引)、预计经过几个站点(扫描行数);ANALYZE 则是装上行车记录仪再跑一趟,预估值与真实值一对照,规划器的「想当然」无处藏身。
与其他知识点的关系
kp-011 的设计结论在此被验证;kp-013 解释计划背后的代价计算;kp-029 的反模式都能在计划里找到对应读数特征。
延伸阅读
- 《高性能 MySQL》(Baron Schwartz 等)查询执行与优化章节
- PostgreSQL 官方文档「Using EXPLAIN」、MySQL 参考手册「EXPLAIN Output Format」(以本地手册核对为准)