执行计划深度解析:从 type 到 Extra,全面掌握 EXPLAIN 的 SQL 调优价值
你是否经常使用 EXPLAIN 来查看 SQL 的执行计划,却未必完全理解每个字段的真正含义?type 到底有几种取值?Extra 中的 Using index、Using where、Using temporary、Using filesort 分别代表什么?key_len 如何计算?filtered 又有什么实际作用?今天我们就来完整解析 EXPLAIN 的输出,帮你彻底掌握 MySQL 查询优化技巧。
简单来说,type 代表 MySQL 查找行的效率:最优的是通过主键或唯一索引直接定位(const),最差的是全表扫描(ALL)。possible_keys 表示可能用到的索引,key 表示实际选用的索引。rows 是预计扫描的行数。filtered 显示存储引擎返回的行中满足后续 WHERE 条件的百分比。Extra 则提供额外操作信息,例如 Using where 表示需要回表过滤,Using temporary 表明使用了临时表。
一、EXPLAIN 输出列完整解读:MySQL 执行计划分析基础
执行 EXPLAIN SELECT ... 语句后,会得到一张包含各字段含义的表:
| 列名 | 含义 | 关键点 |
| id | SELECT 查询的标识序号 | 越大越先执行;相同则从上到下 |
| select_type | 查询类型 | 包括 SIMPLE、PRIMARY、SUBQUERY、DERIVED、UNION 等 |
| table | 表名或别名 | 也可能是临时表名 |
| partitions | 匹配的分区 | 分区表时显示具体分区信息 |
| type | 连接类型(核心指标) | 性能从优到差:system > const > eq_ref > ref > range > index > ALL |
| possible_keys | 可能使用的索引 | 列出候选索引 |
| key | 实际使用的索引 | 若为 NULL 表示未使用索引 |
| key_len | 使用索引的字节长度 | 用于判断联合索引使用了多少列 |
| ref | 索引列与哪个值比较 | 常量 const 或列名 |
| rows | 预估需要扫描的行数 | 数值越小越好 |
| filtered | 存储引擎返回的行中满足剩余条件的比例 | 100% 为最佳 |
| Extra | 额外执行信息 | 包含 Using index、Using where、Using temporary、Using filesort 等 |
虽然表格字段较多,但日常 SQL 优化重点关注 type、key、rows、Extra,以及涉及联合索引的 key_len。
二、type 详解:MySQL 连接类型与性能优化
type 字段表示 MySQL 如何查找表中的行,性能从最优到最差排列如下:
| type | 含义 | 示例 | 出现条件 |
| system | 系统表,仅有一行数据 | 极少出现 | 系统表或 const 的特例 |
| const | 通过主键或唯一索引等值查询,最多匹配一行 | WHERE id = 1 | 主键或唯一索引,且查询条件为常量 |
| eq_ref | 使用主键或唯一索引进行关联,每个关联只返回一行 | JOIN ... ON t1.id = t2.id 且 t2.id 是主键 | 被驱动表使用主键或唯一索引连接 |
| ref | 使用非唯一索引或前缀索引进行等值匹配 | WHERE name = 'abc'(name 有普通索引) | 索引列不是唯一或允许 NULL |
| range | 索引范围扫描 | WHERE id BETWEEN 1 AND 100 或 IN、>、< | 索引列上的范围条件 |
| index | 全索引扫描 | 索引覆盖但缺乏过滤条件 | 遍历整个索引树 |
| ALL | 全表扫描(最差) | 无索引或优化器认为全表更快 | 大表且缺少有效索引 |
优化目标非常明确:至少达到 range 级别,尽量提升到 ref 或 const。
案例:
-- type = ALL 表示全表扫描,性能较差
EXPLAIN SELECT * FROM orders WHERE amount > 100;
-- 添加索引后 type 变为 range
ALTER TABLE orders ADD INDEX idx_amount(amount);
三、Extra 详解:MySQL 优化器隐藏的执行信息
Extra 字段包含了查询执行过程中的额外操作信息,是定位 SQL 性能瓶颈的关键线索:
| Extra 信息 | 含义 | 优劣 | 优化方向 |
| Using index | 使用了覆盖索引,无需回表 | ✅ 好 | 继续保持 |
| Using where | 存储引擎返回后在 Server 层进行过滤 | 普通 | 尝试将过滤条件移到索引中 |
| Using temporary | 使用了临时表(常见于 GROUP BY 或 DISTINCT) | ⚠️ 差 | 优化 GROUP BY/ORDER BY 或添加索引 |
| Using filesort | 无法利用索引排序,需额外排序操作 | ⚠️ 差 | 对 ORDER BY 列加索引 |
| Using index condition | 使用索引下推(ICP) | ✅ 好 | MySQL 5.6+ 自动优化 |
| Using join buffer | 连接使用了 Buffer(Block Nested Loop) | 普通 | 加索引避免 Buffer |
| Impossible WHERE | WHERE 条件永远为假 | 无需优化 | 检查 SQL 逻辑 |
| No tables used | 没有 FROM 或 FROM DUAL | - | - |
注意:Using filesort 并非真的使用文件,而是表示无法利用索引排序,需要在内存或磁盘中进行额外排序。当排序结果集较大时,性能会显著下降。
案例:
-- Using filesort 表示排序需额外处理
EXPLAIN SELECT * FROM orders ORDER BY create_time;
-- 加索引后 Using filesort 消失
ALTER TABLE orders ADD INDEX idx_create_time(create_time);
四、组合索引与 key_len 实战:索引长度计算与最左前缀原则
key_len 表示 MySQL 在执行索引操作时实际使用的字节长度,可用于判断联合索引中具体使用了多少列。
计算规则:
INT 长度为 4 字节,BIGINT 为 8 字节,DATE 为 3 字节,TIMESTAMP 为 4 字节,CHAR(n) 的长度为 n×字符集字节数(utf8mb4 下每个字符 4 字节),VARCHAR(n) 为 n×4 + 2 字节。若允许 NULL,则额外加 1 字节。
来看一个具体例子:联合索引 (user_id, log_date, type),其中 user_id INT NOT NULL (4 字节),log_date DATE NOT NULL (3 字节),type TINYINT (1 字节)。执行 WHERE user_id=1 AND log_date='2026-06-01' 时,key_len=4+3=7,表明使用了前两列。
这里的关键是联合索引使用原则:最左前缀,且中间的列不能跳过。如果跳过了某列,后面的列将不会被使用。
五、filtered 的作用:评估索引过滤效率
filtered 字段表示存储引擎返回的行中,满足后续 WHERE 条件的估算百分比。100% 意味着所有返回行都符合条件。若 filtered 值较小(例如 10%),说明索引过滤后仍有大量行需要进一步过滤,导致回表成本增加。
应用场景:在多表 JOIN 中,驱动表的 filtered 值直接影响被驱动表的读取次数,用于评估整体查询性能。
六、实战案例优化全过程:从 SQL 到索引设计
原始 SQL:
SELECT * FROM orders
WHERE customer_id = 12345
AND status = 'PAID'
AND create_time > '2026-01-01'
ORDER BY create_time DESC
LIMIT 10;
原执行计划:type=ref,key=customer_id,rows≈1000,Extra="Using where; Using filesort"。
问题分析:
虽然使用了 customer_id 索引,但 status 和 create_time 的过滤条件需要在回表后执行。同时,由于 create_time 不在索引中,排序操作无法利用索引,导致 Using filesort。
优化方案: 建立联合索引 (customer_id, status, create_time)。
新执行计划:type=ref,key=联合索引,key_len=4+?,Extra=无 Using filesort(因为索引已排序)。
效果:查询耗时从 0.5 秒降至 0.02 秒。
七、总结与实用检查清单:快速定位 SQL 性能问题
分析 EXPLAIN 输出时,建议按以下顺序排查:
- type: 检查是否出现 ALL 或 index,若有则需添加适当索引。
- key: 若为 NULL,表示未使用索引,需优化查询条件或索引设计。
- rows: 远大于预期时,检查索引选择性是否足够。
- Extra: 出现 Using temporary 或 Using filesort 时,需优化排序或分组逻辑,或添加索引。
- filtered: 低于 30% 时,应考虑创建覆盖更多过滤条件的索引。
