EXPLAIN 是 MySQL 查看查询执行计划的核心命令,用于优化慢查询;其输出字段包括 id(SELECT 序号)、select_type(查询类型)、table(访问表名)、type(连接类型,性能由优到劣为 system→const→eq_ref→ref→range→index→ALL)、possible_keys 与 key(候选及实际索引)、key_len(索引使用字节数)、rows(预估扫描行数)、filtered(条件过滤百分比)、Extra(额外信息如 Using index、Using filesort 等)。

EXPLAIN 是 MySQL 中用于查看 SQL 查询执行计划的核心命令。理解其输出字段,是优化慢查询、识别性能瓶颈的关键一步。下面按 MySQL 8.0+ 常见输出列顺序,逐项说明含义、关键取值及实际判断要点。
id:查询中每个 SELECT 的唯一标识
表示 SELECT 的序列号,从 1 开始递增。相同 id 表示这些操作属于同一层级(如简单查询或 UNION 中的并列子句);不同 id 表示嵌套关系,id 越大越先执行(因为依赖外层结果)。常见情况:
- 单个 SELECT:id 恒为 1
- 子查询(非相关):内层 SELECT id 大于外层,如
(SELECT ... FROM (SELECT ...))可能出现 id=3→2→1 - UNION:第一个 SELECT id=1,后续每个 UNION 分支 id 为 NULL,同时多一行额外记录显示
UNION RESULT
select_type:当前 SELECT 所处的逻辑位置
描述该行对应的是哪种类型的查询片段,直接影响执行方式和优化策略:
- SIMPLE:不包含子查询、UNION 的普通 SELECT
- PRIMARY:最外层的 SELECT(即使有子查询,它也是 PRIMARY)
-
SUBQUERY:在 SELECT 或 WHERE 中的非相关子查询(如
WHERE id IN (SELECT ...)),通常只执行一次 -
DERIVED:FROM 子句中的子查询(如
SELECT * FROM (SELECT ...) AS t),MySQL 会物化为临时表,type 常为ALL,需警惕 - UNION:UNION 中第二个及之后的 SELECT
- UNION RESULT:为合并 UNION 结果而产生的虚拟行,不访问表,可忽略
table:当前操作访问的表名或别名
显示这一行计划作用于哪张表。可能的值包括:
- 真实表名或别名(如
t1、orders o) -
:表示来自第 N 个 DERIVED 子查询生成的临时表 -
:表示 UNION 合并的多个查询结果集(如 ) -
NULL:不访问任何表,例如常量连接(
SELECT 1)或 UNION RESULT 行
type:连接类型,反映访问表数据的方式(性能排序由优到劣)
这是最关键的字段之一,直接体现是否用上索引、扫描范围有多大:
- system:表只有一行(系统表),最快
-
const:主键或唯一索引等值匹配,且匹配到 1 行(如
WHERE id = 5) -
eq_ref:主键/唯一索引做 JOIN 时的高效连接(如
t1.id = t2.t1_id,t2.t1_id 有唯一索引) -
ref:非唯一索引等值匹配(如
WHERE status = 'active',status 有普通索引) -
range:索引范围扫描(如
WHERE id BETWEEN 10 AND 20、WHERE created_at > '2023-01-01') - index:全索引扫描(遍历整棵 B+ 树),比 ALL 快(因不用回表读数据页),但仍是全量扫描
- ALL:全表扫描,最差,应优先优化(检查是否缺索引、索引失效、或统计信息不准)
possible_keys 与 key:索引选择的实际路径
possible_keys 是优化器认为可用的索引列表(基于 WHERE 条件推断);key 是最终选定使用的索引名。
- possible_keys 为空 → 无可用索引,大概率要加索引或重写条件
- key 为空但 possible_keys 非空 → 索引未被选用,常见原因:过滤性差(如低基数列)、统计信息过期、使用了函数/表达式(
WHERE YEAR(create_time) = 2023)、隐式类型转换(WHERE mobile = 13800138000,mobile 是字符串) - key 显示某索引,但 type 不理想(如 key=idx_status,type=ALL)→ 索引失效,需检查条件是否符合最左前缀,或是否存在 OR 导致索引跳过
key_len:实际用到的索引字节数
用于判断联合索引中用了几列,以及是否涉及 NULL 或变长字段:
- 数值越小不一定越好,但能验证索引使用深度。例如联合索引
(a, b, c):
•key_len = 4→ 只用到了 a(假设 a 是 INT NOT NULL)
•key_len = 9→ 用到了 a + b(b 是 VARCHAR(20),字符集 utf8mb4 下单字符最多 4 字节,但这里可能是 VARCHAR(10) + 1 字节长度标识) - 含 NULL 的列,每列额外 +1 字节(标记是否为 NULL)
- 变长字段(VARCHAR、TEXT)会多出 1–2 字节长度信息
rows:预估需要扫描的行数
优化器基于统计信息估算的“这一行计划”将检查多少行。不是返回行数,而是访问开销指标:
- 值越大,IO 和 CPU 成本越高;若远超实际结果集(如 rows=100000,但 SELECT COUNT(*) 只有 100 行),说明统计信息陈旧,可执行
ANALYZE TABLE tbl_name - 多个 JOIN 行的 rows 相乘,大致等于整体扫描量(如 t1.rows=100,t2.rows=500 → 总扫描量约 5 万次)
- 注意:该值不包含过滤后剩余行数,仅指“进入该步骤需读取的行”,后续 WHERE 还会进一步筛选
filtered:该表条件过滤后的行数百分比(MySQL 5.7+)
表示经过 WHERE 条件(不含 JOIN 条件)后,保留的比例(0–100)。例如 filtered=10.00 表示只留下约 10% 的行。
- 结合 rows 使用:
rows × filtered / 100≈ 实际参与下一级的行数 - 若 filtered 极低(如 WHERE gender = 'M'),即使走了索引,仍需大量回表或比较,考虑是否值得索引
Extra:补充说明,含大量性能线索
常见关键值及其含义:
- Using index:覆盖索引,无需回表(SELECT 列全部命中索引),性能好
- Using where:存储引擎返回数据后,Server 层还需额外过滤(常见于索引未覆盖 WHERE 全部条件)
-
Using index condition(ICP):索引条件下推,将部分 WHERE 下推到存储引擎层执行(如
WHERE a = 1 AND b > 10,a,b 有联合索引,b 的范围条件可在引擎层提前过滤),减少回表量 - Using temporary:需创建临时表,常见于 GROUP BY、DISTINCT、UNION、某些 ORDER BY 场景,影响较大
- Using filesort:无法利用索引完成排序,需额外排序操作(内存 or 磁盘),应尽量避免
- Using join buffer(Block Nested Loop):未走索引的 JOIN,启用连接缓冲区,效率较低
-
Impossible WHERE:WHERE 条件恒假(如
WHERE 1=0),直接返回空










