EXPLAIN 的 key 字段仅表示优化器计划使用的索引,不等于实际生效;需结合 type(如 ALL/index 表示未有效过滤)、rows(接近总行数说明未减少扫描)、Extra(如 Using filesort 表示索引不满足排序)综合判断。

EXPLAIN 看不出索引是否“真被用了”
很多人跑 EXPLAIN 看到 key 字段有值,就以为索引生效了。其实不是——key 只表示优化器“打算用哪个索引”,不等于实际执行时走了索引扫描。真正要看是否命中,得结合 type、rows、Extra 一起判断。
常见误判场景:
-
type是ALL或index:说明全表/全索引扫描,索引没起到过滤作用 -
rows接近表总行数:即使key非空,也可能只是用来排序或覆盖,没减少扫描量 -
Extra出现Using filesort或Using temporary:常意味着索引无法满足 ORDER BY 或 GROUP BY,被迫回表或建临时表
type 值直接暴露索引效率等级
type 是判断索引使用质量最直观的字段,从好到差大致是:system ≈ const > eq_ref > ref > range > index > ALL。重点盯住是不是掉到了 range 以下。
典型问题与对策:
- 本该走
ref却变成range:检查 WHERE 条件里有没有对索引列做函数操作,比如WHERE YEAR(create_time) = 2023—— 这会让索引失效 - 联合索引只用上左边几列,但查询条件跳过了中间列:例如索引是
(a,b,c),而条件是WHERE a = 1 AND c = 3,则b之后的索引部分无法利用 -
type是index且rows很大:说明在遍历整个二级索引,可能需要考虑覆盖索引或调整查询字段
FORCE INDEX 有时比优化器更靠谱
MySQL 优化器基于统计信息选索引,但统计可能过期或不准,尤其在数据倾斜、小表 join 大表等场景下,它会“自信地选错”。这时加 FORCE INDEX 是快速验证和临时修复手段。
注意点:
- 语法是
SELECT * FROM t FORCE INDEX (idx_name) WHERE ...,不是USE INDEX(后者只是建议,优化器仍可忽略) - 强制后务必再跑一次
EXPLAIN,确认type和rows是否明显改善 - 上线前要补
ANALYZE TABLE t更新统计信息,否则下次优化器可能又绕回来 - 长期依赖
FORCE INDEX说明索引设计或查询写法有问题,不能当常规解法
information_schema.STATISTICS 查不到“最近用了谁”
想查某个索引“过去一小时被多少查询用过”?information_schema.STATISTICS 只存索引结构,不记录运行时使用痕迹。真要观测,得靠 performance_schema.table_io_waits_summary_by_index_usage(MySQL 8.0+),但默认关闭且开销不小。
更务实的做法:
- 开启慢查询日志 +
log_queries_not_using_indexes = ON,抓出漏网的全表扫描 - 用
SHOW STATUS LIKE 'Handler_read%'对比前后值,看Handler_read_key是否显著增加(说明走了索引查找) - 在测试环境用
pt-index-usage(Percona Toolkit)解析慢日志,反向推导哪些索引从未被用过
索引是否生效,从来不是单看 EXPLAIN 里有没有 key,而是要把 type、rows、Extra 和实际查询模式串起来看;线上观察不到“索引调用次数”,就得靠日志和状态变量交叉印证。


















