SQL 索引失效原因分析与排查方法

7次阅读

索引失效常见于函数操作、隐式类型转换、!=/, like ‘%abc’、联合索引不满足最左前缀或范围查询后列失效;需用explain查看type、key、key_len、rows等字段确认。

SQL 索引失效原因分析与排查方法

索引失效的常见 sql 写法

不是写了 WHERE 条件就一定能走索引。比如对索引列做函数操作:WHERE YEAR(create_time) = 2023mysql 无法使用 create_time 上的索引,因为索引中存的是原始值,不是年份计算结果。类似情况还有 UPPER(name)CONCAT(‘a’, col) 等。

隐式类型转换也会让索引失效。例如字段是 VARCHAR 类型,但查询写成 WHERE col = 123(传了数字),MySQL 会把每行字符串转成数字比对,跳过索引。正确写法是加引号:WHERE col = ‘123’

!= 判断时,除非是覆盖索引且优化器认为高效,否则通常不走索引;LIKE 以通配符开头(如 LIKE ‘%abc’)也无法使用 B+ 树索引的有序特性,只能全表扫描。

联合索引没生效的关键原因

联合索引遵循最左前缀原则。假设建了 (a, b, c) 的联合索引:

  • WHERE a = 1 AND b = 2 ✅ 能用上索引
  • WHERE b = 2 AND c = 3 ❌ 没有 a,跳过整个索引
  • WHERE a = 1 AND c = 3 ⚠️ 只能用到 a,b 和 c 不满足连续最左匹配,c 无法走索引
  • WHERE a = 1 AND b > 10 AND c = 3 ⚠️ a 和 b 可用,但 c 在范围查询后失效(B+ 树中范围之后的列无法二分)

注意:如果 b 是等值条件,c 才可能继续生效;一旦中间出现范围(>、<code>BETWEEN),后续列就“断链”了。

如何确认索引是否真的被用了

别猜,用 EXPLAIN 看执行计划。重点关注这几列:

  • type:最好为 constrefrangeALL 表示全表扫描,index 是全索引扫描,都算失效信号
  • key:显示实际使用的索引名;为 NULL 就代表没走索引
  • key_len:值越小说明用到的索引列越少;对比联合索引定义长度,可判断用了几列
  • rows:预估扫描行数;远大于结果集数量,可能是索引未生效或选择性差

执行 EXPLAIN format=TREE select 还能看到更直观的访问路径树,适合复杂查询分析。

其他容易忽略的失效场景

MySQL 优化器有时会主动放弃索引。比如表很小(几千行),它可能觉得全表扫描比走索引再回表更快;或者索引列重复值太多(如 status 只有 0/1),选择性太低,优化器判定索引效率不如全扫。

OR 条件也需小心:WHERE a = 1 OR b = 2,除非 a 和 b 都有独立索引且满足某些条件,否则大概率走全表扫描。可考虑改写为 union 或补全索引。

还有 ORDER BY + LIMIT 组合:如果排序字段不在索引中,或顺序与索引不一致(如索引是 (a, b),却 ORDER BY b),MySQL 可能放弃索引而用 filesort。

text=ZqhQzanResources