1. MySQL索引失效的底层原理剖析
索引失效的本质原因可以归结为两点:破坏了B+树索引的有序性,或者优化器判断全表扫描的成本更低。要深入理解这个问题,我们需要从MySQL索引的底层实现说起。
1.1 B+树索引的工作原理
MySQL的InnoDB存储引擎默认使用B+树作为索引结构。B+树具有以下关键特性:
- 所有数据都存储在叶子节点,且叶子节点之间通过指针相连形成有序链表
- 非叶子节点只存储键值和子节点指针,不存储实际数据
- 树的高度通常维持在3-4层,保证千万级数据也能在3-4次IO内定位到记录
当执行等值查询(如WHERE id=100)时,MySQL会从根节点开始:
- 比较查询值与节点中的键值范围,确定下一层子节点
- 重复这个过程直到叶子节点
- 在叶子节点中通过二分查找定位具体记录
这种查找方式的效率依赖于索引键的有序性。任何破坏这种有序性的操作都会导致索引失效。
1.2 优化器的成本计算逻辑
MySQL优化器在选择执行计划时,会基于成本模型进行估算,主要考虑:
- IO成本:读取数据页的代价
- CPU成本:处理数据的代价
- 内存成本:使用临时表或排序的代价
当优化器判断使用索引的成本高于全表扫描时(通常发生在预计要访问超过30%的数据行时),就会放弃使用索引。这就是为什么某些情况下即使技术上可以使用索引,MySQL也会选择全表扫描。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 索引失效的九大场景深度解析
2.1 OR条件导致的索引失效
OR条件导致索引失效的根本原因是"离散条件合并"问题。假设我们有以下查询:
sql复制SELECT * FROM orders WHERE user_id = 100 OR amount > 1000;
即使user_id和amount都有索引,MySQL也需要:
- 使用user_id索引查找user_id=100的记录
- 使用amount索引查找amount>1000的记录
- 对两个结果集进行去重合并
这个过程的成本可能比直接全表扫描更高,特别是当OR条件中有一个字段没有索引时,MySQL只能选择全表扫描。
优化方案:
- 将OR改写为UNION:
sql复制SELECT * FROM orders WHERE user_id = 100
UNION
SELECT * FROM orders WHERE amount > 1000;
- 确保OR条件中的所有字段都有合适的索引
- 考虑使用复合索引覆盖多个OR条件
2.2 隐式类型转换导致索引失效
当查询条件的类型与列定义类型不匹配时,MySQL会进行隐式类型转换。这种转换相当于在列上使用了函数,破坏了索引的有序性。
常见陷阱:
- 字符串列与数字比较:
WHERE phone = 13800138000 - 日期列与字符串比较:
WHERE create_time = '2023-01-01' - ENUM列与数字比较:
WHERE status = 1(status是ENUM类型)
优化方案:
- 严格匹配
