explain 是判断 mysql 是否使用索引最直接有效的方式,需重点观察 type、key、rows 和 extra 四列,并结合索引定义、隐式转换、函数使用等场景综合分析索引是否真正高效。

用 EXPLAIN 看执行计划,这是最直接有效的判断方式
MySQL 不会主动告诉你“用了哪个索引”,但 EXPLAIN 会把优化器的决策摊开给你看。在你要分析的 select 语句前加 EXPLAIN,比如:
EXPLAIN SELECT * FROM users WHERE email = 'a@b.com';
重点关注这四列:
-
type:值为ALL→ 全表扫描,没走索引;const/eq_ref/ref/range→ 通常走了索引 -
key:非NULL(如显示idx_email)→ 实际使用了该索引 -
rows:预估扫描行数,远小于表总行数才说明索引过滤有效;接近总数可能已失效 -
Extra:出现using filesort或Using temporary→ 即使走了索引,排序或分组仍可能回表或额外开销
注意:key 非空 ≠ 高效,必须结合 type 和 rows 综合判断。
常见索引失效场景,一查就中招
很多 SQL 表面看着合理,其实根本没走索引。典型例子包括:
- 对索引列做隐式类型转换:
WHERE phone = 13800138000(phone是VARCHAR),应写成WHERE phone = '13800138000' - 在索引列上使用函数:
WHERE YEAR(create_time) = 2023→ 改用范围查询:WHERE create_time >= '2023-01-01' AND create_time - 模糊查询以
%开头:LIKE '%abc'→ 无法利用索引,可考虑全文索引或倒排结构 - 联合索引未满足最左前缀:
INDEX(a, b, c),但只查WHERE b = 1→ 不走索引;必须从a开始才有机会命中 - 索引列参与计算:
WHERE score + 10 > 90→ 改为WHERE score > 80
辅助验证:查索引是否存在、是否被真正用过
光看 EXPLAIN 还不够,得确认基础条件是否成立:
- 用
SHOW INDEX FROM users;查表上有没有对应索引,以及索引列顺序是否匹配查询条件 - 用
performance_schema.table_io_waits_summary_by_index_usage(MySQL 5.6+)看某索引实际被调用频次 - 查询
sys.schema_unused_indexes(MySQL 5.7+ sys schema)识别长期零使用的冗余索引 - 开启慢查询日志(
slow_query_log=ON),设long_query_time=1,捕获那些因未走索引而变慢的语句
这些不是替代 EXPLAIN 的手段,而是帮你定位“为什么明明建了索引却没被选中”。
进阶调试:用 OPTIMIZER_TRACE 看优化器怎么想的
当 EXPLAIN 显示没走索引,但你确信它“应该走”,就可以启用优化器跟踪:
SET optimizer_trace="enabled=on"; SELECT * FROM users WHERE age > 30; SELECT * FROM information_schema.optimizer_trace; SET optimizer_trace="enabled=off";
返回的 json 里会明确写出:“为什么放弃索引 idx_age?因为预估扫描行数占比太高,成本高于全表扫描”。这种细节,EXPLAIN 不会告诉你。
真正容易被忽略的是:索引是否“被选中”和是否“高效”是两回事——type=range + rows=10万 的查询,跟 type=ref + rows=2 的性能差距,可能比走不走索引还大。