EXPLAIN 执行计划 完全精通指南


一、什么是 EXPLAIN?

EXPLAIN 是 MySQL 自带的 SQL 体检工具,也是所有 SQL 优化的第一步。只需记住一句话:

在 SELECT 语句前面加 EXPLAIN,就能看到 MySQL 打算怎么执行这条 SQL。

sql 复制代码
EXPLAIN SELECT * FROM zzw_juanzhu_xuesheng WHERE id = 1;

执行后会输出一张表格,包含这些列:

列名 作用
id 查询的执行顺序编号
select_type 查询类型(简单查询/子查询/UNION等)
table 正在访问的表名
partitions 匹配的分区(分区表才显示)
type ⭐ 访问类型,最核心的性能指标
possible_keys 可能用到的索引
key 实际使用的索引
key_len 使用到的索引长度(字节)
ref 与索引比较的列或常量
rows MySQL 预估需要扫描的行数
filtered 按条件过滤后剩余行数的百分比
Extra 额外信息(是否回表、是否排序等)

核心要点:

  • EXPLAIN 不会真正执行 SQL,不会查询数据,不会修改数据
  • 它只是提前告诉我们:MySQL 打算怎么执行这条 SQL
  • 通过体检结果,能精准发现:有没有走索引、是不是全表扫描、索引有没有失效、SQL 为什么卡顿

🔥 面试高频:所有 MySQL 性能优化,第一步一定是 EXPLAIN 分析执行计划。


二、select_type:查询类型(理解子查询必看)

select_type 表示查询的类型,看懂它才能分析复杂 SQL(子查询、UNION)。

select_type 含义 场景
SIMPLE 简单查询,不包含子查询和 UNION 最常见的单表查询
PRIMARY 最外层的主查询 有子查询时,最外层就是 PRIMARY
SUBQUERY 子查询中的第一个 SELECT SELECT 或 WHERE 中的子查询
DERIVED 派生表(FROM 子句中的子查询) FROM (SELECT ...) AS t
UNION UNION 中第二个及之后的 SELECT UNION 联合查询
UNION RESULT UNION 合并后的结果集 从临时表中读取合并结果
DEPENDENT SUBQUERY 依赖外部查询的子查询 子查询引用了外部表的字段
MATERIALIZED 物化子查询(MySQL 5.6+) 子查询结果先存入临时表

理解要点:简单查询全部显示 SIMPLE,出现 DERIVED 和 DEPENDENT SUBQUERY 就要警惕性能问题。


三、核心字段:type(看懂它,就懂 90% 优化)

type 是执行计划中最重要的字段,代表 SQL 的查询效率等级。

性能排序(必背):

sql 复制代码
system → const → eq_ref → ref → range → index → ALL
  └─────────────── 越往左性能越好 ──────────

下面从最优到最差,逐一讲解:


1. system(最优,但极少见)

适用场景:表只有一行数据(系统配置表等)

通俗理解:整张表就一条记录,直接拿就行了,比 const 还快。

📌 实际开发中几乎遇不到,了解即可。


2. const(最优级别,性能天花板)

适用场景:单表查询 + 主键/唯一索引 + 精准等值匹配

sql 复制代码
EXPLAIN SELECT * FROM zzw_juanzhu_xuesheng WHERE id = 1;
-- type = const(id 是主键)

通俗理解:主键是唯一的,MySQL 不需要遍历任何数据,直接通过索引定位到唯一一行,速度最快。

限制条件:只能用于单表查询,多表联表不会出现该级别。

评价:最优 SQL,线上满分级别。


3. eq_ref(联表查询最优)

适用场景:多表 JOIN 联表查询,被关联的表通过主键/唯一索引精准匹配

sql 复制代码
EXPLAIN SELECT * FROM orders o 
JOIN users u ON o.user_id = u.id;
-- users 表的 type = eq_ref(u.id 是主键)

通俗理解:两张表关联时,一对一精准匹配数据,没有多余扫描,是联表查询的最好状态。

评价:联表场景满分级别。


4. ref(线上最标准、最常用)

适用场景:普通索引 + 等值查询,可匹配多条数据

sql 复制代码
-- xuesheng_name 建有普通索引
EXPLAIN SELECT * FROM zzw_juanzhu_xuesheng WHERE xuesheng_name = '张三';
-- type = ref

通俗理解:通过普通索引快速筛选数据,虽然可能匹配多行,但完全不用全表扫描,性能稳定可控。

注意 :此时 EXPLAIN 的 ref 列会显示 const,表示用常量值来匹配索引。如果是联表查询,ref 列会显示关联表的字段名。

评价:线上业务推荐的标准写法,性能合格。


5. range(范围查询,可控安全)

适用场景:索引字段做范围匹配

支持所有语法><>=<=BETWEENINLIKE '前缀%'

sql 复制代码
EXPLAIN SELECT * FROM zzw_juanzhu_xuesheng WHERE xuesheng_name LIKE '王%';
-- type = range

重点避坑:很多人以为 range 是慢查询,这是错的!小范围查询完全没问题,只有一次性查询数万行大数据,才会变慢。

评价:可控级别,日常业务完全可用。


6. index(全索引扫描,高危!)

适用场景:无需过滤数据,只需遍历索引

两种触发情况

情况一:只查询索引字段,不需要回表

sql 复制代码
-- xuesheng_name 有索引
EXPLAIN SELECT xuesheng_name FROM zzw_juanzhu_xuesheng;
-- type = index,Extra = Using index(覆盖索引)

情况二:ORDER BY 索引字段

sql 复制代码
EXPLAIN SELECT * FROM zzw_juanzhu_xuesheng ORDER BY xuesheng_name;
-- 也可能触发 type = index

通俗解释:MySQL 不会扫描整张数据表,但会从头到尾遍历整棵二级索引树,属于全量遍历,不是精准查询。

核心特点

  • 比全表扫描快(索引文件体积远小于数据表)
  • 本质还是全量扫描,数据量大了一定会卡顿
  • Extra 显示 Using index,代表无需回表,是覆盖索引

⚠️ 评价:小表可用,大表绝对禁止。


7. ALL(全表扫描,线上红线禁止)

通俗解释:从头到尾遍历整张数据表,不使用任何二级索引过滤,性能最差。

高频出现场景(必记)

场景 示例
查询字段无索引 WHERE xuesheng_yizizhu = 'xx'(字段无索引)
索引失效:左模糊 LIKE '%王'
索引失效:全模糊 LIKE '%王%'
索引失效:函数运算 WHERE YEAR(create_time) = 2024
索引失效:隐式转换 WHERE phone = 13800138000(phone 是 varchar)
无条件全表查询 SELECT * FROM 表

评价:线上绝对禁止,属于不合格 SQL。


四、key vs possible_keys:可能 vs 实际(关键对比)

字段 含义
possible_keys MySQL 认为可能用到的索引列表
key MySQL 实际选择使用的索引

当两者不一致时,说明

  • 优化器在多个索引中做了选择
  • 可能选错了索引(统计信息不准)
  • 可以用 FORCE INDEX 强制指定索引来对比验证
sql 复制代码
-- 对比测试
EXPLAIN SELECT * FROM table WHERE a = 1 AND b = 2;           -- 看优化器选哪个
EXPLAIN SELECT * FROM table FORCE INDEX(idx_a) WHERE a = 1;  -- 强制用索引a

五、key_len:联合索引用了几个字段(面试必考)

key_len 表示 MySQL 实际使用的索引长度(字节数),是判断联合索引用了几个字段的关键指标。

示例 :假设联合索引 idx_name_age(name, age)

  • name 是 varchar(50),utf8mb4 编码,允许 NULL → 长度 = 50 × 4 + 2 + 1 = 203
  • age 是 int,允许 NULL → 长度 = 4 + 1 = 5
sql 复制代码
EXPLAIN SELECT * FROM users WHERE name = '张三' AND age = 25;
-- key_len = 208(203 + 5),说明联合索引两个字段都用上了
sql 复制代码
EXPLAIN SELECT * FROM users WHERE name = '张三';
-- key_len = 203,说明只用了 name 一个字段

面试要点:key_len 越大,说明用到的索引字段越多,过滤效果越好。


六、Extra:额外状态标记(完整版)

标记 含义 评价
Using index 覆盖索引,不需要回表查数据 ✅ 优秀
Using index condition 使用了索引下推(ICP,MySQL 5.6+) ✅ 优秀
Using where 使用了 WHERE 过滤 正常(但配合 type=ALL 就是坏消息)
Using filesort 需要额外排序(没走索引排序) ⚠️ 需要优化
Using temporary 需要创建临时表(GROUP BY/DISTINCT) ⚠️ 需要优化
Using join buffer 联表用了 Join Buffer(被驱动表没走索引) ⚠️ 需要优化
Impossible WHERE WHERE 条件永远为假,不会返回任何数据 检查 SQL 逻辑
Select tables optimized away 查询被优化器直接优化掉了 ✅ 最优(如 SELECT MAX(id) FROM t

🔥 面试高频Using index(覆盖索引)和 Using index condition(索引下推)的区别。


七、rows:预估扫描行数(重要但别全信)

  • rows 是 MySQL 基于统计信息估算的值,不是精确值
  • 数值越小性能越好,越大越需要优化
  • 如果统计信息过期,rows 可能严重偏离实际值

更新统计信息

sql 复制代码
ANALYZE TABLE zzw_juanzhu_xuesheng;

八、EXPLAIN 进阶用法

命令 作用 版本要求
EXPLAIN FORMAT=JSON 输出 JSON 格式,信息比表格更详细 MySQL 5.6+
EXPLAIN FORMAT=TREE 树形输出,直观显示执行路径 MySQL 8.0.16+
EXPLAIN ANALYZE 真正执行 SQL 并输出每步实际耗时 MySQL 8.0.18+
sql 复制代码
-- 最推荐:MySQL 8.0.18+ 用这个,能看到实际耗时
EXPLAIN ANALYZE SELECT * FROM users WHERE name = '张三';

⚠️ 注意:EXPLAIN ANALYZE 会真实执行 SQL,生产环境慎用!


九、四大终极误区(解决 99% 的疑问 + 面试必考)

误区 1:EXPLAIN 中 key=NULL,代表完全没用到索引?(面试高频坑)

标准答案:错误(仅限 InnoDB 引擎)

InnoDB 是聚簇索引引擎,整张表的所有数据都存在主键(聚簇索引)中。我们看到的 type=ALL 全表扫描,底层本质是遍历主键聚簇索引树,不可能脱离索引。

正确定义key=NULL 仅仅代表本次查询没有使用二级索引做过滤和定位,不是没有用到任何索引。


误区 2:InnoDB 和 MyISAM 全表扫描有什么区别?(面试对比考点)

两个引擎核心差异,直接记住结论:

引擎 全表扫描本质
InnoDB 数据和索引一体,全表扫描 = 遍历主键聚簇索引
MyISAM 数据和索引文件完全分离,全表扫描直接读原始数据文件,完全不碰任何索引

误区 3:字段有索引,无条件查询就一定很快?

完全错误。

不管字段有没有索引,无 WHERE 条件的查询都是全量扫描:

sql 复制代码
SELECT name FROM 表;   -- type=index  全索引遍历(扫描二级索引树)
SELECT * FROM 表;      -- type=ALL    全表遍历(扫描聚簇索引/数据文件)

区别的本质:

  • type=index:扫描二级索引树(只含索引字段+主键,体积小)
  • type=ALL:扫描聚簇索引/数据文件(包含全部字段,体积大)

两者只是扫描文件大小不同,数据过万后都会变慢,线上一律禁止无条件全表查询。


误区 4:无索引的 LIKE '王%' 不算慢查询?

小表(4000 行)场景下,这条 SQL 是百分百全表扫描,但因为耗时极低,没触发慢查询阈值,所以不记录。

核心结论:小表无感知,上万数据量必然卡顿,变成标准慢 SQL。


十、索引失效的完整场景(面试必考 100%)

失效场景 示例 原因
左模糊/全模糊 LIKE LIKE '%王'LIKE '%王%' 无法利用索引有序性
函数运算 WHERE YEAR(create_time) = 2024 对字段做了函数运算
运算表达式 WHERE id + 1 = 10 字段参与了运算
隐式类型转换 WHERE phone = 13800138000(phone 是 varchar) 字符串字段用数字查询
OR 条件有非索引字段 WHERE name = '王' OR age = 20(age 无索引) OR 两边都需要索引
违反最左前缀 联合索引 (a,b,c),查询 WHERE b=1 AND c=2 跳过了 a 字段
范围查询右边失效 联合索引 (a,b,c),查询 WHERE a=1 AND b>10 AND c=3 b 用了范围,c 失效
!= 或 <> WHERE status != 1 不等于不走索引
IS NULL / IS NOT NULL 视数据分布而定,可能不走索引 优化器判断
NOT IN / NOT EXISTS 通常不走索引 无法利用索引

十一、实战排查流程(直接套用)

遇到慢 SQL,按以下步骤排查:

sql 复制代码
1. EXPLAIN 看 type
   ├─ type=ALL/index → 全量扫描,进入第 2 步
   ├─ type=range/ref → 走索引,检查扫描行数
   └─ type=const/eq_ref → 最优,无需优化

2. 检查 WHERE 条件字段
   ├─ 无索引 → 加索引
   └─ 有索引但没用上 → 进入第 3 步

3. 检查是否索引失效
   ├─ 函数运算/隐式转换 → 修改 SQL 写法
   ├─ 左模糊 LIKE → 考虑全文索引或 ES
   └─ 联合索引未满足最左前缀 → 调整索引或 SQL

4. 检查 key 和 possible_keys 是否一致
   └─ 不一致 → 统计信息可能过期,ANALYZE TABLE

5. 检查 Extra
   ├─ Using filesort → ORDER BY 字段加索引
   ├─ Using temporary → GROUP BY/DISTINCT 字段加索引
   └─ Using join buffer → 被驱动表关联字段加索引

6. rows 过大
   └─ 考虑覆盖索引、分页、数据归档

十二、优化口诀(直接套用)

级别 目标 说明
✅ 优先追求 const / eq_ref / ref / 小范围 range 线上标准
⚠️ 尽量避免 index 全索引遍历 大表禁止
❌ 绝对禁止 ALL 全表扫描 线上红线
⚠️ 需要优化 Using filesortUsing temporary 影响性能

十三、复盘 + 面试速记(必背)

  1. 核心本质:EXPLAIN 看的是 MySQL 预执行方案,不运行 SQL
  2. type 优先级必背system → const → eq_ref → ref → range → index → ALL
  3. 最大易错点 :InnoDB 全表扫描是遍历主键索引,key=NULL 只是不用二级索引
  4. key_len 面试题:联合索引用了几个字段,看 key_len 的值
  5. select_type:出现 DERIVED、DEPENDENT SUBQUERY 要警惕
  6. 禁用场景 :无条件全表查询、左模糊 %xxx、无索引查询、索引字段做函数运算
  7. 面试加分项type=indextype=ALL 都是全量扫描,大表都不允许
  8. 覆盖索引 :Extra 显示 Using index,不需要回表,性能最优
  9. 索引下推 :Extra 显示 Using index condition,MySQL 5.6+ 优化特性
  10. EXPLAIN ANALYZE:MySQL 8.0.18+ 可以看真实执行耗时
相关推荐
AI行业学习3 小时前
CC-Switch v3.16.1 官方下载 | 安装配置详细教程【2026.6.10】
java·开发语言·vue.js·python·mysql·eclipse·html
用户3074596982073 小时前
乐观锁与悲观锁
mysql
AOwhisky4 小时前
学习自测与解析:MySQL第五、六、七期核心知识点详解
运维·数据库·笔记·学习·mysql·云计算
梦想的旅途25 小时前
企业微信外部群主动调用:RPA 接口与官方 API 的技术边界
网络·mysql·自动化·企业微信·rpa
ULIi096kr7 小时前
MySQL查看表创建时间、修改时间、最后更新时间(精准排查僵尸表)
数据库·mysql
折哥的程序人生 · 物流技术专研7 小时前
Tomcat 严重警告:JDBC 驱动未注销 + 工作线程泄漏 —— 原因、影响与彻底修复(生产级终极指南)
java·运维·数据库·mysql·oracle·tomcat
拄杖忙学轻声码7 小时前
mysql脚本查询数据,符合指定条件的排在数据列表最前面,实现方式
mysql
济*沧*海8 小时前
MySQL分库分表实战解析
mysql
天海华兮8 小时前
MySQL知识点 覆盖索引、MVCC、存储引擎、事务锁、性能优化等核心点
mysql·事务·日志·索引·mvcc·存储引擎·执行计划