真实面经题目 · 原创解析
MySQL 查询为什么可能不走索引?
MySQL 不走索引通常不是“索引失效”这么简单,而是优化器认为使用索引的总代价不划算,或者查询条件的写法让索引无法被有效利用。面试中要从索引可用性、选择性、代价模型、联合索引规则和执行计划验证几层回答。
真实面经题目 · 原创解析
MySQL 不走索引通常不是“索引失效”这么简单,而是优化器认为使用索引的总代价不划算,或者查询条件的写法让索引无法被有效利用。面试中要从索引可用性、选择性、代价模型、联合索引规则和执行计划验证几层回答。
可以从两类原因回答:第一类是索引本身虽然存在,但查询条件无法按索引有序结构定位,比如对索引列做函数、隐式类型转换、前置模糊匹配、违反联合索引最左前缀等;第二类是索引能用,但优化器认为不如全表扫描,例如数据量小、条件选择性差、需要大量回表、统计信息不准。实际排查时我会先看 EXPLAIN,确认 type、key、rows、filtered、Extra,再结合表数据分布和 SQL 写法判断是“不具备索引访问路径”,还是“具备但代价更高”。
MySQL 是否走索引最终由优化器决定,它会估算使用索引扫描、回表、排序、临时表和全表扫描的成本。如果条件命中大量数据,使用二级索引可能需要频繁回表,随机 I/O 和额外读取反而比顺序扫描更贵。尤其是返回比例很高、字段又不覆盖索引时,优化器可能直接选择全表扫描,这不是索引坏了,而是成本模型认为收益不明显。
B+Tree 索引依赖列值的有序排列来快速定位。如果在索引列上做函数、表达式计算、隐式类型转换,或者使用前置通配的模糊匹配,优化器很难直接利用原始索引顺序。例如把列值转换后再比较,本质上要逐行计算结果,索引中保存的原始值就无法用于快速定位。面试时可以强调:尽量让索引列保持“裸列”参与比较。
联合索引不是多个单列索引的简单叠加,而是按定义顺序组成的复合排序结构。只有查询条件从最左列开始连续匹配时,才能有效利用前缀部分;中间跳过某一列,后面的列通常不能继续用于定位。遇到范围条件后,后续列往往只能参与过滤或排序优化,无法继续缩小索引扫描范围。因此联合索引设计要结合等值条件、范围条件、排序字段和查询频率。
索引适合快速过滤少量行,如果某个字段重复值很多,例如状态、性别、布尔标记等,单独建索引未必有明显效果。查询命中比例越高,扫描索引再回表的成本越接近甚至超过全表扫描。更合理的做法通常是把低选择性字段放入联合索引中,与高选择性字段组合使用,或者设计覆盖索引,减少回表成本,而不是简单地为每个条件字段都建单列索引。
排查不走索引不能只靠猜,需要看执行计划里的 key、possible_keys、rows、filtered、Extra 等信息。possible_keys 有值但 key 为空,说明理论可用但未被选择;key 有值但 rows 很大,可能索引过滤效果差;Extra 出现 Using filesort 或 Using temporary,说明排序或分组没有被很好利用。统计信息陈旧也会导致误判,必要时需要更新统计信息,并结合真实数据分布判断优化器选择是否合理。
因为普通 B+Tree 索引保存的是列的原始值顺序,函数计算后的结果不在这个索引结构中。数据库如果要比较函数结果,往往需要逐行计算,无法直接按原索引定位范围。
如果命中大量行,二级索引扫描后还要大量回表,随机读取成本很高。此时顺序扫描整张表可能更简单、更稳定,优化器就可能放弃索引。
通常范围列后面的列很难继续用于缩小扫描范围,但可能用于索引条件下推、过滤或覆盖。能否发挥作用要结合具体版本、执行计划和索引顺序判断。
先看 EXPLAIN,确认 possible_keys、key、type、rows 和 Extra;再检查 SQL 是否有函数、隐式转换、前置模糊匹配、联合索引顺序不匹配;最后看数据分布、选择性和统计信息是否导致成本估算变化。