MySQL EXPLAIN执行计划到索引优化完全解读

作者:技术拾光

时间:2026-07-21

浏览:175

EXPLAIN是MySQL查看执行计划的命令,通过解析查询展示索引使用、联表方式及预估扫描行数。关键字段包括id(执行顺序)、select_type(查询类型,DEPENDENTSUBQUERY为性能杀手,需改JOIN)、type(访问类型,效率从高到低为system、const、eq_ref、ref、range),据此可定位瓶颈并优化索引。

文章目录

前言

从事数据库调优多年,要说哪款工具最实用,EXPLAIN 绝对位列前茅。任意一条 SELECT 查询,只需在前面加上 EXPLAIN,MySQL 就会将执行计划完整呈现出来——使用哪些索引、如何联表、预估扫描行数、是否存在隐藏瓶颈,一目了然。

MySQL EXPLAIN执行计划到索引优化完全解读

本文从实战角度出发,详细拆解 EXPLAIN 输出中的每个字段,同时提供一套可直接应用的优化判断标准。

一、EXPLAIN 是什么?

执行 EXPLAIN SELECT ... 后,MySQL 并不会真正运行该查询,而是返回一个表格形式的执行计划,清晰展示每一步的执行方式。

EXPLAIN SELECT * FROM user WHERE id = 1;

输出结果可能为一行或多行——每一行代表一个步骤,例如单表访问、子查询、UNION 合并等。

二、字段详解(按重要性排序)

1. id —— 步骤编号

  • 数字越大,越先执行

  • 数字相同,从上往下执行

  • NULL:最后合并步骤(如 UNION)

注意:子查询不一定比主查询后执行,DERIVED 表(派生表)反而会先执行。

2. select_type —— 查询类型

值含义性能警示
SIMPLE简单查询,无子查询、无UNION 正常
PRIMARY外层主查询-
SUBQUERY普通子查询 可能慢
DERIVEDFROM 中的子查询(派生表) 一般慢
UNIONUNION 中第二个及之后的查询
DEPENDENT SUBQUERY相关子查询(依赖外层) 极慢,必须改 JOIN
UNCACHEABLE SUBQUERY每次执行都要重算 极慢

重点盯防:DEPENDENT SUBQUERY 是性能杀手,遇到后基本需要改为 JOIN 才能解决。

3. table —— 访问对象

可能是真实的表名,也可能是类似 (id=2 的派生表)或 这样的临时结构。

4. type —— 访问类型 ★★★★

该字段重要性排前三,直接反映索引的使用效率。性能从好到差排列如下:

级别含义说明
system表只有一行系统表,极少见
const主键/唯一索引等值查询极快,最多匹配一行
eq_ref联表时被驱动表用主键/唯一索引联表最优
ref普通索引等值查询好
range索引范围查询(>, <, IN, BETWEEN)尚可
index扫描整个索引树比全表快一点,但仍需回表
ALL全表扫描 最差,大表必崩

优化红线:如果看到 type = ALL 且表数据量超过 10 万行,无需犹豫,立即加索引。

5. possible_keys —— 可能使用的索引

列出候选索引。若该列为 NULL,说明该表没有可用索引,必须创建。

6. key —— 实际使用的索引 ★★★★

  • 为 NULL:未走索引

  • 需与 possible_keys 对比查看:有时存在索引,但 MySQL 未选择(原因隐藏在 Extra 中)

7. key_len —— 使用的索引长度(字节)

该字段有一个妙用:判断联合索引用到了几列。

例如,联合索引 (a, b, c):

  • key_len = 4:只用了 a(INT)

  • key_len = 8:用了 a、b(均为 INT)

  • key_len = 13:用了 a、b、c(INT + INT + VARCHAR(10))

常见类型的长度参考(utf8mb4):

  • INT:4 字节

  • BIGINT:8

  • VARCHAR(100):100 * 4 + 2 = 402 字节

  • DATETIME:5(5.6+)、8(旧版)

8. ref —— 与索引比较的对象

  • const:常量,例如 WHERE id = 1

  • db.table.column:联表时另一张表的列

  • NULL:非等值查询

9. rows —— 预计扫描行数 ★★★★

注意,这是 MySQL 的估算值,并非精确值。数值越大,查询越慢。优化的最终目标是让 rows 持续减小。

10. filtered —— 过滤后剩余比例(%)

存储引擎返回数据后,经过 WHERE 条件过滤后剩余的比例。越低越好,说明索引筛选能力强。

示例:rows = 1000, filtered = 10 → 预计最终返回 100 行。

11. Extra —— 额外信息 ★★★★★

该字段如同宝藏,直接暴露性能陷阱。

好信号

值含义
Using index覆盖索引,不回表,极快
Using where有 WHERE 条件过滤

警告信号(需优化)

值含义处理建议
Using index condition索引条件下推(ICP)尚可,但可检查索引顺序
Using temporary使用了临时表 GROUP BY 无索引,必须修改
Using filesort文件排序(内存/磁盘) ORDER BY 无索引,必须添加
Using join buffer联表无索引,使用缓存 必须为被驱动表加索引

危险信号

值含义
Using temporary; Using filesortGROUP BY + ORDER BY 完全未走索引
Impossible WHEREWHERE 条件永远为假
No tables used无 FROM 表

三、实战:5 分钟定位慢查询

拿到一条慢 SQL,按照以下顺序逐列审查 EXPLAIN 结果:

第一步:先看type

  • const/eq_ref/ref → 安全

  • range → 尚可

  • index/ALL → 必须优化

第二步:再看key

  • 为 NULL → 未建索引,立即添加

  • 不为 NULL → 检查是否选对了索引

第三步:看Extra

  • 出现 filesort/temporary/join buffer → 必须优化

  • 出现 Using index → 优秀

第四步:估算rows

  • 预期只返回几条,但 rows 显示几万 → 索引失效或未走对索引

四、典型案例优化全过程

原始慢查询

EXPLAIN SELECT * FROM order WHERE user_id = 123 ORDER BY create_time DESC;

执行计划:

  • type: ALL(全表扫描)

  • rows: 500000

  • Extra: Using filesort(文件排序)

  • key: NULL

结论:全表扫描 50 万行,再在内存中排序 → 极慢。

添加索引

ALTER TABLE order ADD INDEX idx_user_time (user_id, create_time);

优化后执行计划

EXPLAIN SELECT * FROM order WHERE user_id = 123 ORDER BY create_time DESC;
  • type: ref(走索引)

  • key: idx_user_time

  • rows: 15

  • Extra: Using index condition(无 filesort)

效果:从 50 万行扫描降至 15 行,排序也消失了。

五、总结:EXPLAIN 优化口诀

EXPLAIN 看计划,逐行拆开查:
type 是关键,ALL 是红线;
key 不为空,才算走索引;
rows 尽量小,越大越危险;
Extra 藏魔鬼,temporary 必须改,filesort 立即办;
覆盖索引最理想,Using index 放心赞。

写在最后

EXPLAIN 堪称 MySQL 性能优化的入场券。掌握它后,在索引设计、SQL 改写、架构选型方面就能拥有自己的判断力。

但也需明确,EXPLAIN 并非万能:

  • 它给出的是估算,而非精确值

  • 不统计存储引擎之外的消耗(网络、客户端)

  • 不反映锁等待、CPU 开销

因此,EXPLAIN + 慢查询日志 + 性能监控,才是完整的 MySQL 调优体系。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章