MySQL EXPLAIN 详解
不是一种索引,是一种查询方式,查询的结果字段在索引树都包含;2.
一、EXPLAIN 的两个变种
1. EXPLAIN EXTENDED
在基础 EXPLAIN 之上,额外提供查询优化的细节信息:
- 新增
filtered列(百分比值):rows * filtered/100可估算与前一个表(id 更小的表)连接的行数; - 执行后通过
SHOW WARNINGS命令可查看优化后的 SQL 语句,看到优化器的调整逻辑。
2. EXPLAIN PARTITIONS
专为分区表设计,新增 partitions 字段,显示查询会访问的分区名称。
注意:MySQL 5.7 及以上版本已整合这两个变种的功能:无需额外指定 EXTENDED/PARTITIONS,直接执行 EXPLAIN 命令即可看到 filtered 和 partitions 字段。
二、EXPLAIN 输出列详解
示例:
1. id 列(执行顺序)
- id 是
SELECT语句的序列号,有几个SELECT就有几个 id; - id 越大执行优先级越高,越先执行;
- id 相同时:从上到下依次执行;
- id 为 NULL:最后执行(通常是 UNION 结果集的汇总操作)。
2. select_type 列(查询类型)
| 常见类型 | 含义 | 示例 |
|---|---|---|
| SIMPLE | 简单查询,查询语句不包含子查询、无 UNION | SELECT * FROM film WHERE id=1 |
| PRIMARY | 复杂查询中最外层的 SELECT | 包含子查询 / UNION 等复杂场景中的外层查询部分 |
| SUBQUERY | 子查询,SELECT 子句中的子查询(非 FROM 子句内) | SELECT (SELECT 1 FROM actor) FROM film |
| DERIVED | 衍生查询 ,FROM 子句中的子查询(衍生表),MySQL 会生成临时表存储结果 | SELECT * FROM (SELECT * FROM film) AS der |
| UNION | UNION 中第二个及之后的 SELECT | SELECT 1 UNION ALL SELECT 2 |
| UNION RESULT | UNION 结果的汇总查询(id 为 NULL) | 上述 UNION 语句的最终结果集 |
示例:
3. table 列
显示当前行操作的表名(或衍生表别名、UNION 临时表),也就是SQL语句查询的表。
4. type 列(SQL语句执行时查询行的范围,访问类型 / 性能优先级)
核心意义:MySQL 查找表中行的方式,决定查询效率,从最优到最差排序:NULL > system > const > eq_ref > ref > range > index > ALL
| 类型 | 含义 | 示例场景 |
|---|---|---|
| NULL | 优化阶段直接得到结果,甚至不需要访问表 或 索引 | SELECT MIN(id) FROM film(直接从索引取最小值) |
| system | const 的特例,表本身就仅有 1 行数据(系统表级别) | SELECT * FROM sys_config(仅 1 行配置表) |
| const | 主键或唯一索引与常量匹配时,只有 1 条数据匹配(只返回一行结果) | SELECT * FROM film WHERE id=1 |
| eq_ref | 联表查询中,主键 / 唯一索引全匹配,仅 1 行匹配结果 | SELECT * FROM film f JOIN film_actor fa ON f.id=fa.film_id(fa.film_id 为主键) |
| ref | 普通索引匹配(使用的索引不唯一),可能返回多行结果 | SELECT * FROM film WHERE name='film1'(name 为普通索引) |
| range | 索引范围扫描(IN、BETWEEN、>、< 等) | SELECT * FROM actor WHERE id > 10 |
| index | 全索引扫描(扫描二级索引全量,但是不回表。可用覆盖索引优化,也就是让二级索引覆盖所有查询字段) | SELECT name FROM film(name 为二级索引) |
| ALL | 全表扫描(扫描聚簇索引所有的叶子节点,因为聚簇索引包含整张表所有数据,读取的磁盘数大消耗时间多,所以效率低,性能最差,应避免) | SELECT * FROM actor(无索引条件) |
注意:聚簇索引(主键索引)包含整张表数据,IO 成本高;INDEX 类型扫描二级索引全量,二级索引仅含索引列 + 主键,IO 成本低于 ALL。
5. possible_keys 列
查询时可能用到的索引列表(由MySQL 基于查询条件推导)。
注意:有时会出现possible_keys 有值但 key 为 NULL的情况,原因是表数据量过小,MySQL优化器认为全表扫描比走索引更高效,最终选择了全表扫描。
6. key 列(SQL语句实际使用的索引)
- 显示 MySQL 实际选择的索引(NULL 表示未用索引);
- 强制使用 / 忽略索引:
FORCE INDEX/IGNORE INDEX,例如:sql
EXPLAIN SELECT * FROM film FORCE INDEX (idx_name) WHERE name='film1'; - 优化逻辑:当结果集可从主键索引和二级索引获取时,优先选二级索引(体积更小,IO 成本低)。
7. key_len 列(索引使用字节数)
核心作用
根据计算索引使用的字节数,推断SQL语句实际用到了哪些索引(使用联合索引时)。
计算规则(MySQL 5.0.3+)
| 数据类型 | 字节数计算规则 |
|---|---|
| CHAR(n) |
字符集为 utf8: utf8mb4: |
| VARCHAR(n) |
utf8: utf8mb4: |
| TINYINT | 1 字节(允许 NULL 则 + 1) |
| SMALLINT | 2 字节(允许 NULL 则 + 1) |
| INT | 4 字节(允许 NULL 则 + 1) |
| BIGINT | 8 字节(允许 NULL 则 + 1) |
| DATE | 3 字节(允许 NULL 则 + 1) |
| DATETIME | 8 字节(允许 NULL 则 + 1) |
| TIMESTAMP | 4 字节(允许 NULL 则 + 1) |
示例:联合索引 idx_film_actor_id (film_id, actor_id)(均为 INT类型且非空),若 key_len=4,说明仅用到 film_id 列;若 key_len=8,说明两列都用到。
注意:允许 NULL 则 + 1的原因是预留一个字节记录是否为空。
8. ref 列(索引匹配的条件)
显示索引匹配的 “参照物”,常见值:
const:常量匹配(如film_id=2);- 表字段名:联表匹配(如
film.id = film_actor.film_id)。
示例:mysql> explain select * from film_actor where film_id = 2;(那么ref的值就是const(常量),如果是字段那就是字段名)
9. rows 列
MySQL 估算的扫描行数(非结果集行数),数值越小越优。
注意:该值是优化器基于统计信息的估算值,并不是真实的精确值。
10. Extra 列(关键优化提示)
| 常见取值 | 含义 | 优化建议 |
|---|---|---|
| Using index | 覆盖索引(查询字段均在索引中,无需回表) | 推荐!尽量通过覆盖索引减少回表 |
| Using where | WHERE 过滤,但查询列未被索引覆盖(需回表) | 补充索引覆盖查询字段 |
| Using index condition | 索引下推(ICP):WHERE 条件部分推到存储引擎层过滤 | 正常优化手段,无需调整 |
| Using temporary | 创建临时表(如 DISTINCT/ORDER BY 无索引) | 加索引消除临时表 |
| Using filesort | 外部排序(未用索引排序) | 加排序字段索引,避免磁盘排序 |
| Select tables optimized away | 聚合函数(MIN/MAX)直接从索引获取结果 | 最优状态,无需优化 |
注意:
1. 覆盖索引定义:不是一种索引,是一种查询方式,查询的结果字段在索引树都包含;
2. 回表:通过二级索引查询到字段无法覆盖需要查询的字段时,MySQL会通过二级索引找到对应的主键值,再根据主键值回到主键索引(聚簇索引)中查询完整记录,这个过程就叫回表。
更多推荐



所有评论(0)