最值得盯住的是type、key、rows、extra四个字段:type为all或index表示全表扫描,key为空说明未走索引,rows远大于结果行数提示索引失效,extra出现using filesort或temporary表明排序/分组未用索引。

EXPLAIN 输出里哪些字段最值得盯住
关键不是看全字段,而是盯住 type、key、rows、Extra 这四个。它们直接暴露索引是否生效、扫描范围有多大、有没有隐式转换或临时表。
type 值为 ALL 或 index 时基本等于全表扫描;key 为空说明没走索引;rows 显示预估扫描行数,如果远大于结果集行数(比如查 10 行却扫 10 万行),就是典型索引失效;Extra 出现 Using filesort 或 Using temporary 意味着排序/分组没走索引。
为什么加了索引还是走不了?常见坑点
索引失效不是“有没有”,而是“用没用上”。MySQL 对索引的使用非常挑剔,稍有偏差就退化成全表扫描。
- WHERE 条件中对索引字段做函数操作:比如
WHERE YEAR(create_time) = 2023,哪怕create_time有索引也无效 - 隐式类型转换:字段是
VARCHAR,但查询条件写成WHERE user_id = 123(数字),MySQL 会把所有值转成数字比对,索引失效 - 联合索引顺序错位:索引是
(a, b, c),但查询只用了b或b AND c,前面的a缺失,索引无法命中 - LIKE 左模糊:
WHERE name LIKE '%abc'无法利用索引,LIKE 'abc%'才可以
EXPLAIN FORMAT=JSON 能多看出什么
默认的表格输出太简略,EXPLAIN FORMAT=JSON 会暴露优化器真正做的决策,比如是否使用了索引合并、是否下推了 WHERE 条件、是否做了子查询物化。
重点关注 "used_columns"(实际参与索引查找的列)、"pushed_cond"(下推到存储引擎的过滤条件)、"rows_examined_per_scan"(单次扫描真实读取行数)——这些比 rows 更接近实际执行开销。
示例:
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE status = 'paid' AND amount > 100;
执行计划和真实执行不一致?别只信 EXPLAIN
EXPLAIN 是预估,不是实测。它不考虑数据分布倾斜、缓存状态、并发锁等待这些运行时因素。
如果 EXPLAIN 显示走了索引、rows 很小,但查询依然慢,就得看真实执行:
- 用
SHOW PROFILE或 performance_schema 查 CPU/IO 时间花在哪 - 用
SELECT ... INTO DUMPFILE或慢日志里的Query_time和Rows_examined对比预估与实际差异 - 确认统计信息是否过期:
ANALYZE TABLE orders可能让执行计划更准
最常被忽略的是:EXPLAIN 不反映 MVCC 版本链遍历开销,高并发更新场景下,即使索引高效,也可能卡在回滚段清理或可见性判断上。











