MySQL 索引为什么没有被使用?面试时别只背“索引失效”

无敌码农编辑部 · 发布于 2026-10-29

先给一个面试回答

“索引没被使用”不一定表示索引坏了。MySQL 会根据可用访问路径和估算成本选择执行计划。我会先看查询能否利用索引的有序结构,再检查数据分布、返回行数和回表成本,最后用 EXPLAIN 验证。常见原因包括对索引列做不匹配的函数运算、联合索引条件不适合定位、前导通配符,以及优化器判断全表扫描更划算。

回答的重点是区分两种情况:一是无法高效利用某个索引定位数据,二是能够使用,但优化器选择了其他路径。

为什么索引也有成本

InnoDB 的二级索引记录包含索引列和主键值。查询需要其他列时,可能还要通过主键读取整行,通常称为回表。当条件命中大量行时,扫描二级索引再频繁回表,未必优于顺序读取整张表。

因此,“给每个条件加索引”不是完整方案。索引也占空间,增加插入和更新的维护成本。判断是否值得,需要同时考虑查询频率、选择性、返回字段和写入负担。

同一条 SQL 在小表与大表上的计划也可能不同;本地少量测试数据得出的结论,不能直接当作生产环境的事实。

三类常见追问

对列使用函数为什么可能影响索引

假设索引建立在 created_at 上,条件写成 DATE(created_at) = '2026-10-01',普通列索引往往不能直接按这个表达式定位。可以改成明确的半开区间:

SELECT id, created_at
FROM orders
WHERE created_at >= '2026-10-01 00:00:00'
  AND created_at < '2026-10-02 00:00:00';

这里的日期边界必须符合业务时区。函数索引或生成列可以支持特定表达式,但需要版本和索引定义配合,不能概括成“任何函数都会让所有索引失效”。

联合索引是不是不满足最左前缀就一定无用

对于 (tenant_id, status, created_at),已知 tenant_id 通常更容易在索引中缩小范围。只有后面的字段时,访问方式可能不同。不过不要把最左前缀口诀理解成绝对结论:覆盖索引扫描、特定条件下的跳跃扫描等,都可能让执行计划仍然出现这个索引。

更准确的表述是:不满足前缀条件时,通常不能按预期进行高效定位,最终要查看具体计划和版本行为。

LIKE、类型转换和低选择性怎么解释

LIKE 'abc%' 可能形成索引范围,而 LIKE '%abc' 通常无法用普通 B-tree 索引定位开头。字符串列与数字比较还可能触发类型转换,影响索引使用;参数类型应与列类型一致。状态、性别这类取值少的字段并非永远不能建索引,但单列索引的收益可能有限,需要结合其他过滤条件和覆盖需求判断。

实际排查顺序

先取得完整 SQL、绑定参数、表结构和索引定义,再用 EXPLAIN 查看访问方式、候选索引、实际选择的索引、估算行数及 Extra。检查条件是否发生隐式转换,再评估索引列顺序和返回字段。

MySQL 8.0.18 起支持 EXPLAIN ANALYZE,能对比估算与实际执行情况,但它会真正执行语句,应先在安全环境中使用。发现估算明显偏差时,再考虑统计信息是否需要更新;不要把 FORCE INDEX 当作第一反应。

本文 SQL 是用于解释查询改写的示例,未在读者的数据集上执行,也不代表性能提升结论。验证时应准备有代表性的数据,并对比执行计划、读取行数和耗时。

面试中怎样讲清楚

按“现象 → 假设 → 证据 → 修改 → 结果”回答。比如先描述查询变慢,再提出回表成本偏高的假设,通过计划验证,最后讨论覆盖索引或缩小查询范围。没有真实测量时,直接说明这是分析思路,不编造优化倍数。

参考资料

内容用于技术学习与求职准备,实际方案请结合版本和业务环境验证。如发现错误,欢迎通过关于页面反馈。

继续阅读