Skip to content

索引失效的场景

索引失效场景

数据库索引(如 MySQL 的 B+ 树索引)旨在加速查询,但在以下情况下会失效,导致全表扫描: 1. 条件中使用函数或运算:索引列被加工,无法直接匹配。 2. 隐式类型转换:查询条件与索引列类型不一致。 3. LIKE 模糊查询前缀通配:如 %abc,无法利用索引。 4. OR 条件跨非索引列:部分条件无索引。 5. 不是最左前缀匹配:复合索引未按顺序使用。 6. 查询范围过大:优化器认为全表扫描更快。 7. NULL 或 IS NULL 判断:某些情况下索引失效。

核心点

  • 索引失效本质是优化器无法利用索引树。

1. 失效场景详解

(1) 条件中使用函数或运算

  • 原因
  • 对索引列应用函数或运算(如 func(col)col + 1),改变了原始值,索引无法直接查找。
  • 示例
CREATE INDEX idx_name ON user(name);
SELECT * FROM user WHERE UPPER(name) = 'JOHN'; -- 索引失效
SELECT * FROM user WHERE name + 1 = 2; -- 索引失效
  • 解决
  • 避免加工索引列:
SELECT * FROM user WHERE name = 'John';

(2) 隐式类型转换

  • 原因
  • 查询条件与索引列类型不匹配,触发转换,索引失效。
  • 示例
CREATE INDEX idx_phone ON user(phone); -- phone 是 varchar
SELECT * FROM user WHERE phone = 12345; -- 数字转字符串,失效
  • 解决
  • 保持类型一致:
SELECT * FROM user WHERE phone = '12345';

(3) LIKE 模糊查询前缀通配

  • 原因
  • % 在开头(如 %abc),无法确定范围,索引失效。
  • 示例
CREATE INDEX idx_name ON user(name);
SELECT * FROM user WHERE name LIKE '%son'; -- 失效
  • 解决
  • 后缀通配有效:
SELECT * FROM user WHERE name LIKE 'John%'; -- 有效
  • 或用全文索引。

(4) OR 条件跨非索引列

  • 原因
  • OR 连接的条件中,若部分列无索引,优化器可能放弃索引。
  • 示例
CREATE INDEX idx_age ON user(age);
SELECT * FROM user WHERE age = 25 OR name = 'John'; -- name 无索引,可能失效
  • 解决
  • 确保所有条件列有索引,或拆分查询。

(5) 不是最左前缀匹配

  • 原因
  • 复合索引要求查询条件遵循最左原则,未满足则失效。
  • 示例
CREATE INDEX idx_composite ON user(age, name);
SELECT * FROM user WHERE age = 25; -- 有效
SELECT * FROM user WHERE name = 'John'; -- 失效
  • 解决
  • 按索引顺序查询:
SELECT * FROM user WHERE age = 25 AND name = 'John';

(6) 查询范围过大

  • 原因
  • 数据选择性低(如返回 80% 数据),优化器认为全表扫描更快。
  • 示例
CREATE INDEX idx_age ON user(age);
SELECT * FROM user WHERE age > 0; -- 数据量大,失效
  • 解决
  • 优化条件提高选择性,或强制索引(FORCE INDEX)。

(7) NULL 或 IS NULL 判断

  • 原因
  • B+ 树索引对 NULL 处理有限,IS NULL 可能失效。
  • 示例
CREATE INDEX idx_name ON user(name);
SELECT * FROM user WHERE name IS NULL; -- 可能失效
  • 解决
  • 避免 NULL,或用默认值替代。

2. 原理分析

  • B+ 树索引
  • 索引是按列值排序的树结构,查询需快速定位。
  • 加工列值、类型转换等破坏了直接匹配能力。
  • 优化器决策
  • MySQL 优化器基于成本(Cost-Based Optimizer),若索引开销高于全表扫描,则放弃索引。
  • 执行计划
  • EXPLAIN 查看:
EXPLAIN SELECT * FROM user WHERE UPPER(name) = 'JOHN';
-- type: ALL (全表扫描)

3. 延伸与面试角度

  • 与数据库版本
  • MySQL 8.0+ 对函数索引支持改善。
  • 实际应用
  • 订单查询:避免 WHERE YEAR(order_date) = 2023
  • 优化建议
  • 创建覆盖索引。
  • 重写 SQL 避免函数。
  • 面试点
  • 问“场景”时,提函数和最左原则。
  • 问“排查”时,提 EXPLAIN

总结

索引失效多因查询条件破坏索引树的匹配性(如函数、类型转换、最左前缀)或优化器选择全表扫描。理解 B+ 树和优化器原理是关键,解决靠调整 SQL 或索引设计。面试时,可提示例或 EXPLAIN,展示理解深度。