关键看type、key、key_len、rows和Extra:key为NULL说明未用索引;type为ALL/index低效,ref/range/const且key有值才有效命中;key_len反映实际索引字节数;Extra含Using index表示覆盖索引;type=ALL常见于函数操作、隐式转换、计算表达式、低选择性或统计过期;联合索引需满足最左匹配,范围查询后列失效;rows是估算值,关注趋势而非绝对数。

Explain 输出中哪些字段说明索引被真正用上了
关键看 type、key、key_len、rows 和 Extra 这五个字段。如果 key 为 NULL,基本可以确定没走索引;type 是 ALL 或 index(全表/全索引扫描)也大概率是低效的;而 type 为 ref、range、const 且 key 显示具体索引名,才说明索引被有效命中。
注意 key_len 的值:它反映实际用于查找的索引字节数。比如 VARCHAR(100) 字段建了普通索引,但查询条件是 WHERE name = 'abc',key_len 可能只显示 4(假设 utf8mb4 下 3 字符 + 1 字节长度标识),而不是理论最大值 400 —— 这说明 MySQL 没有浪费地读整个索引前缀。
-
Extra出现Using index:表示走了覆盖索引,无需回表 -
Extra出现Using where; Using index:也是覆盖索引,且 WHERE 条件在索引层就完成了过滤 -
Extra出现Using filesort或Using temporary:即使走了索引,排序或分组仍可能触发额外开销
为什么加了索引,Explain 却显示 type=ALL
常见原因不是索引没建,而是查询写法或数据特征导致优化器放弃使用索引:
- WHERE 条件对索引列做了函数操作,如
WHERE YEAR(create_time) = 2023—— 索引失效,改用create_time BETWEEN '2023-01-01' AND '2023-12-31' - 隐式类型转换,如
WHERE user_id = '123'(user_id 是 INT),MySQL 会把索引列转为字符串比较,无法走索引 - 索引列参与了计算,如
WHERE score * 2 > 100,必须重写为WHERE score > 50 - 索引选择性太低(如性别字段只有 'M'/'F'),MySQL 认为全表扫描反而更快,可通过
FORCE INDEX强制验证,但不建议线上滥用 - 统计信息过期,执行
ANALYZE TABLE table_name更新后重新EXPLAIN
联合索引的最左匹配原则在 Explain 中怎么验证
看 key_len 和 key 就能判断是否用到了联合索引的前缀部分。例如联合索引 idx_a_b_c (a,b,c):
-
WHERE a = 1 AND b = 2→key_len显示 a+b 的字节长度,key显示idx_a_b_c,说明用了前两列 -
WHERE a = 1 AND c = 3→ 虽然key可能仍是idx_a_b_c,但key_len只反映 a 的长度,Extra里会出现Using where,说明 c 没走索引,只是在 a 匹配的结果集上做后过滤 -
WHERE b = 2 AND c = 3→key很可能为NULL,因为跳过了最左列 a,整个联合索引不可用
特别注意:范围查询(>、<、BETWEEN)之后的列无法继续走索引。例如 WHERE a = 1 AND b > 2 AND c = 3,c 不会命中索引。
Explain 结果里的 rows 值到底准不准
rows 是 MySQL 优化器基于统计信息估算的“需要检查的行数”,不是精确值,尤其在大表或数据分布不均时偏差可能很大。它受 innodb_stats_persistent、采样页数量(innodb_stats_sample_pages)影响。
不要把它当绝对性能指标,而应关注趋势对比:比如加索引前后 rows 从 100 万降到 1000,说明索引生效;但如果从 100 万变成 80 万,可能只是统计抖动,需结合实际执行时间判断。
容易忽略的一点:rows 只反映单次驱动表的扫描量。在 JOIN 场景下,若驱动表 rows=100,被驱动表每次匹配都查 10 行,则实际 IO 可能是 100 × 10 = 1000 次 —— rows 不体现嵌套循环的放大效应。


















