判断索引是否生效应优先查看EXPLAIN的key和type字段:key非NULL且type不为ALL或index即走索引;type=ref/eq_ref表示高效索引访问,range为范围扫描,ALL为全表扫描需优化;key为NULL说明索引未被选用。

直接看 EXPLAIN 的 key 和 type 字段
这是最快速、最可靠的判断方式。只要 key 列非 NULL,且 type 不是 ALL 或 index(全索引扫描),基本就走索引了。
-
type = ref或eq_ref:走了二级索引或主键/唯一索引,效果好 -
type = range:走了索引范围扫描(如WHERE age BETWEEN 25 AND 35) -
type = ALL:没走索引,全表扫描——性能杀手,优先优化 -
key为空或显示NULL:即使有索引,也没被选中
示例:
EXPLAIN SELECT * FROM users WHERE email = 'a@b.com';
如果返回中 key: idx_email 且 type: ref,说明成功命中索引;若 key: NULL + type: ALL,就得继续排查原因。
为什么有索引却没走?常见踩坑点
建了索引 ≠ 查询自动用索引。MySQL 优化器会基于成本估算决定是否使用,而“成本低”不等于“有索引就一定用”。
-
隐式类型转换:
WHERE user_id = '123'(user_id是INT)→ 触发全表扫描,因为字符串转整数会让索引失效 - 对索引列用函数:
WHERE YEAR(create_time) = 2024→ 索引无法下推,改写为create_time BETWEEN '2024-01-01' AND '2024-12-31' - 前导模糊匹配:
WHERE name LIKE '%abc'→ B+树无法从左匹配,索引失效;LIKE 'abc%'可用 - 索引选择性太低:比如给只有两个值的
gender列建索引,优化器大概率放弃它 - 数据量极小(如几百行):优化器可能认为全表扫描比回表+索引查找更快
辅助验证:查索引是否存在 & 是否被统计
有时你以为有索引,其实根本没建,或者建错列了。用这两条命令交叉验证:
- 查索引定义:
SHOW INDEX FROM users;
确认Column_name和Key_name是否符合预期(比如你查email,但索引建在user_email上) - 查索引统计信息(反映优化器“认知”):
SELECT * FROM mysql.innodb_index_stats WHERE database_name = 'your_db' AND table_name = 'users';
若n_diff_pfx01(基数)异常低,说明统计过期,可手动更新:ANALYZE TABLE users;
进阶定位:用 optimizer_trace 看优化器真实决策
当 EXPLAIN 显示没走索引,但你觉得“它应该走”,就可以打开优化器跟踪,看它到底怎么想的:
SET optimizer_trace="enabled=on"; SELECT * FROM users WHERE status = 1 AND created_at > '2025-01-01'; SELECT * FROM information_schema.optimizer_trace\G SET optimizer_trace="enabled=off";
重点关注 analyzing_range_alternatives 和 using_index_for_order_by 段落,里面会明确写出:“该索引成本为 123.45,全表扫描成本为 89.12,故选择后者”。这才是真正的原因,不是玄学。
注意:optimizer_trace 是会话级的,别忘了关;而且它只对当前会话生效,不能查别人跑的 SQL。










