一、EXPLAIN 的两个变种

1. EXPLAIN EXTENDED

在基础 EXPLAIN 之上,额外提供查询优化的细节信息:

  • 新增 filtered 列(百分比值):rows * filtered/100 可估算与前一个表(id 更小的表)连接的行数;
  • 执行后通过 SHOW WARNINGS 命令可查看优化后的 SQL 语句,看到优化器的调整逻辑。

2. EXPLAIN PARTITIONS

专为分区表设计,新增 partitions 字段,显示查询会访问的分区名称。

注意:MySQL 5.7 及以上版本已整合这两个变种的功能:无需额外指定 EXTENDED/PARTITIONS,直接执行 EXPLAIN 命令即可看到 filteredpartitions 字段。

二、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:3*n(3为utf8存储一个字符需要的字节数

utf8mb4:4*n(允许 NULL 则 + 1)

VARCHAR(n)

utf8:3*n + 2(2 字节存长度);

utf8mb4:4*n + 2(允许 NULL 则 + 1)

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会通过二级索引找到对应的主键值,再根据主键值回到主键索引(聚簇索引)中查询完整记录,这个过程就叫回表。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐