判断索引是否失效需综合key、type、extra和rows:key为null表明未用索引;type为all/index/range但rows接近总数说明实际扫描量大;extra中using filesort/using temporary或仅有using where且type=all提示失效;rows严重偏离实际值(如超3倍)则统计信息可能过期。

看 key 字段是否为 NULL
key 是最直接的判断依据:它显示优化器实际选用的索引名。如果值是 NULL,说明这条查询完全没走任何索引,大概率已失效。
但要注意陷阱:key 非空 ≠ 索引高效。比如联合索引 (a, b, c),查询 WHERE a = 1 AND c = 3 可能显示 key = union_idx,但 c 实际无法利用(跳过了 b),这时得结合 key_len 和 type 综合判断。
常见误判场景:
- 字段有索引,但查询用了函数(如
WHERE DATE(create_time) = '2024-01-01')→key显示索引名,但实际失效 - 字符串字段类型是
VARCHAR,但传入数字(如WHERE mobile = 13800138000)→ 发生隐式转换,key可能非空,但rows暴涨 - 联合索引最左列未出现在
WHERE中(如只查b = 2)→key为NULL,明确失效
重点盯 type 字段的扫描级别
type 表示访问表的方式,它比 key 更能反映“用了多少索引”。从优到劣排序是:const ≈ eq_ref > ref > range > index > ALL。
真正危险的是后三档:
-
ALL:全表扫描,索引彻底失效(除非表极小,优化器主动放弃) -
index:扫描整个索引树,不是全表但开销接近,尤其当索引不覆盖查询字段时,还要回表 -
range虽属“生效”,但如果rows接近总行数(比如查 100 万行中的 95 万),InnoDB 很可能退化为聚簇索引全扫描,锁范围和性能表现等同于ALL
例如:WHERE status IN ('draft', 'pending') 在低区分度字段上,type 可能是 range,但 rows 显示 80 万 → 实际就是扫表。
别忽略 Extra 里的隐藏警告
Extra 是索引是否“真有效”的照妖镜。几个关键信号:
-
Using where; Using index:表面正常,但如果type是index或ALL,说明 MySQL 先扫了整张表/索引,再用WHERE过滤——索引只是摆设 -
Using filesort或Using temporary:大概率伴随type = ALL或大量回表,排序/分组无法利用索引完成 - 空值或仅有
Using where,且type = ALL:典型“索引失效+全表扫描”,高风险锁表
特别注意:Using index condition 是好信号(ICP 优化开启),说明部分过滤下推到了存储引擎层;但若同时出现 Using filesort,仍表示 ORDER BY 无法走索引。
验证 rows 是否严重偏离实际
rows 是优化器预估的扫描行数,它和真实数据分布强相关。如果 rows 远大于你预期的匹配行数(比如查某天数据,rows 显示 50 万,但 SELECT COUNT(*) 实际只有 200 条),基本可断定统计信息过期或索引被降级使用。
排查步骤:
- 执行
SELECT COUNT(*) FROM t WHERE xxx对比rows值,误差超 3 倍就要怀疑 - 运行
ANALYZE TABLE t更新统计信息,再跑一次EXPLAIN看变化 - 检查是否有隐式转换或函数导致优化器误判(如
WHERE create_time > '2024-01-01'写成> 20240101,触发数字转字符串)
真正线上问题里,rows 失准比 key = NULL 更隐蔽也更常见——它让你误以为索引在工作,实则已在拖慢整个查询。











