最关键看type、key、rows、Extra四个字段:type为ALL表示全表扫描;key为NULL说明未走索引;rows预估扫描行数,越大性能越差;Extra中Using filesort或temporary需警惕。

EXPLAIN 输出字段里哪些最关键
看 EXPLAIN 结果,别一上来就扫全列。重点关注这四个字段:type、key、rows、Extra。
type 值从好到差一般是:const ≈ eq_ref > ref > range > index > ALL。出现 ALL 就是全表扫描,基本等于没走索引。
key 显示实际用到的索引名。如果为 NULL,说明没走索引;如果和你预期的不一样,得查是否索引失效或优化器选错了。
rows 是 MySQL 预估需要扫描的行数,不是结果集行数。这个值越大,I/O 越高;比实际表行数还大,往往意味着索引没生效或条件写法有问题。
Extra 里要警惕:Using filesort(排序没走索引)、Using temporary(用了临时表)、Using index condition(ICP 开启,算好现象)、Using where; Using index(覆盖索引,理想状态)。
为什么加了索引却显示 type=ALL
常见原因不是索引没建,而是查询条件触发了索引失效。比如:
- 对索引字段做函数操作:
WHERE YEAR(create_time) = 2023→ 改成WHERE create_time >= '2023-01-01' AND create_time -
隐式类型转换:
WHERE user_id = '123'(user_id 是 INT)→ 字符串强制转数字,索引失效 - LIKE 以通配符开头:
WHERE name LIKE '%abc'→ 无法用 B+ 树索引快速定位 - 联合索引没用最左前缀:
INDEX (a, b, c),但只查WHERE b = 1 AND c = 2→ 不走索引
检查时先用 SHOW CREATE TABLE 确认索引定义,再对照 WHERE 条件逐字核对是否满足最左匹配和无破坏性操作。
EXPLAIN FORMAT=JSON 能看出什么额外信息
默认的表格输出太简略。EXPLAIN FORMAT=JSON 会返回嵌套结构,揭示优化器真实决策路径。重点关注:
-
used_key_parts:实际用到的联合索引字段,比如["a","b"]表示只用了前两列 -
filtered:表示该表条件过滤后剩余行数占比(百分比),值越低说明过滤效率越差 -
attached_condition:下推到存储引擎层的条件,和Using index condition对应 -
rows_examined_per_scan:单次扫描读取的行数,比普通rows更贴近真实 I/O
例如执行:
EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE status = 'paid' AND amount > 100;
如果返回中 "used_key_parts": ["status"] 但没包含 amount,说明联合索引可能缺了第二列,或者 amount 的范围查询截断了索引使用。
ORDER BY 和 GROUP BY 容易被忽略的索引细节
很多人只关注 WHERE 是否走索引,但排序和分组同样依赖索引结构。关键点:
-
ORDER BY a, b要高效,索引必须是(a, b)或更长前缀,且方向一致(不能ORDER BY a ASC, b DESC配INDEX (a ASC, b ASC)) -
GROUP BY同理,如果没索引支持,就会触发Using temporary; Using filesort - 当
WHERE + ORDER BY共存时,优先满足 WHERE 的最左前缀,再看能否覆盖 ORDER BY。比如WHERE a = 1 ORDER BY b,索引(a, b)就比(b, a)合适得多 - 注意 NULL 值处理:如果字段允许 NULL,而索引没特别设计,
ORDER BY col DESC可能无法利用索引(因 NULL 默认排在最前,与 DESC 冲突)
一个典型陷阱:SELECT * FROM logs WHERE app = 'web' ORDER BY id DESC LIMIT 20。即使有 INDEX (app, id),若 id 是主键自增,MySQL 仍可能放弃该索引而走主键扫描——因为优化器认为按主键倒序取 20 行比用二级索引回表更便宜。这时得加 FORCE INDEX 或调整索引顺序验证。










