SQL 索引失效场景解析(8大失效场景与优化器成本决策)

SQL 中”建了索引却不走”通常不外两类根因:一类是索引列的原始值或顺序被破坏,B+ 树在物理上无法被遍历;另一类是索引能用、但优化器算了一笔账,认为回表成本比全表扫描还高,主动放弃。据 CSDN 一篇索引失效根因分析总结,前者(函数、计算、类型转换、字符集转换、前导通配符、跳过最左列、范围截断)是绝对失效,与数据量无关;后者(回表过多、OR 一侧无索引、否定条件、统计信息过期)是成本决策,是否失效取决于数据分布。排查的唯一标准是 EXPLAIN,看 type 是否为 ALL、key 是否为 NULL。

一、根因一:B+ 树有序性被破坏

索引存的是列的原始值,且按前缀有序排列。查询条件一旦改变这个原始值,或在树上找不到起点,索引就物理不可用。这类失效应能无关、必然发生。

失效场景 错误写法 失效原因 修复写法
索引列用函数 WHERE DATE(create_time)='2026-06-01' 函数改变了列原值 改成范围:BETWEEN '2026-06-01 00:00:00' AND '2026-06-01 23:59:59'
索引列做运算 WHERE age + 1 = 20 计算后的值在树中无序 计算移到右侧:age = 19
隐式类型转换 phone 为 VARCHAR,WHERE phone = 13800138000 相当于对列 CAST,破坏有序性 保持类型一致:phone = '13800138000'
前导模糊 WHERE name LIKE '%张' 前缀未知,无法定位起点 用右匹配:LIKE '张%'
违反最左前缀 联合索引 (a,b,c) 只查 b 跳过最左列找不到入口 补齐前导列或调整索引顺序
范围截断后续列 (a,b) 中 a > 1 AND b = 2 范围区间内 b 无序 等值列放范围列前,建 (b,a)
字符集不一致 两表关联字段编码不同 CONVERT 作用在索引列 统一字符集与排序规则

据腾讯云开发者社区的解析,这七类可统一归于一句话:被索引字段的隐式转换、表达式计算、函数计算,都会破坏 B+ 树叶子节点的有序性,执行器无法判断原索引树还能否被检索,于是放弃使用。MySQL 8.0 引入了 Skip Scan,对”跳过最左列”有例外优化,但不能依赖。

二、根因二:优化器的成本决策

即使索引可用,优化器基于代价估算也可能选全表扫描。这类失效不绝对,换批数据或更新统计信息就可能反转。

回表成本过高。当索引筛选后仍需回表取大量行、且这些行在磁盘上分散时,随机 IO 成本可能超过顺序全扫。据 youngju.dev 的 PostgreSQL 案例,用 EXPLAIN 看 Heap Fetches 是否过大,或建覆盖索引(INCLUDE)避免回表,可恢复索引使用。

OR 一侧无索引。据 CSDN 多个面试题解析,WHERE a=1 OR b=2 只要 b 没有索引,整个查询可能直接全表扫描;应给两侧都建索引、或拆成 UNION。

否定条件(!=、<>、NOT IN、IS NOT NULL)。这类条件通常命中大多数行,优化器认为走索引回表不如全扫。可改写正向范围或用 LEFT JOIN ... IS NULL 替代 NOT IN。

统计信息过期。据 CSDN 根因分析,统计偏差会让优化器误判成本,用 ANALYZE TABLE 更新即可。

三、低基数列与 NULL 的影响

据 cnblogs 与 CSDN 的整理,当索引列选择性极差(如 gender 只有两个值)时,索引过滤效果弱,优化器倾向全表扫描——这类低基数列通常不适合建单列索引。索引列含大量 NULL 也可能影响优化器选择,核心业务字段建议设 NOT NULL 并给默认值。另据 youngju.dev,列的 correlation(物理顺序与索引顺序的相关性)接近 1 时,即便范围查询也利于用索引,接近 0 时则相反。

四、用 EXPLAIN 验证的四步法

据 CSDN 与腾讯云资料,排查固定动作如下:

  1. 在语句前加 EXPLAIN(PostgreSQL 加 ANALYZE 看真实执行)。
  2. 看 type:目标是 ref/range/index,出现 ALL 即全表扫描。
  3. 看 key:若为 NULL,说明没用上索引。
  4. 看 rows 与 Extra:rows 过大、Extra 出现 Using filesort/Using temporary 提示需调整索引或写法。

五、修复索引失效的通用顺序

  1. 把索引列上的函数、运算、类型转换全部移除或移到等号右侧。
  2. 模糊查询改右匹配,避免前导 %。
  3. 联合索引查询补齐最左列,等值列排范围列前。
  4. 统一关联字段的类型、字符集、排序规则。
  5. 用覆盖索引消除回表;低基数列不建单列索引。
  6. 跑 EXPLAIN 确认 key 非空、type 非 ALL,必要时 ANALYZE TABLE。

常见问题(FAQ)

LIKE 以 % 开头一定不能走索引吗?
普通 B+ 树索引下确实失效;PostgreSQL 可用 pg_trgm 扩展建 GIN 索引支持子串搜���。

!= 和 NOT IN 永远不走索引吗?
不是。它们属成本决策类,若命中行很少、统计准确,优化器仍可能选索引,应以 EXPLAIN 为准。

隐式转换为什么毁索引?
类型不匹配时引擎对列做 CAST,等价于对列用函数,破坏了索引有序性,与显式函数失效同理。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 qiqicto@qq.com 举报,一经查实,本站将立刻删除。
赞 (0)
小码农的头像小码农认证作者

相关推荐

返回顶部