web学习笔记编程技术学习与分享

索引失效的七个常见原因

2026 年 09 月 02 日约 3 分钟阅读 后端与数据库

建了索引查询仍然全表扫描,多数时候不是 MySQL 的 bug,而是查询写法让索引用不上。下面是出现频率最高的七种情况。

1. 对索引列做函数或运算

-- 失效:索引列被包裹在函数里
SELECT * FROM orders WHERE DATE(created_at) = '2026-09-01';
-- 有效:改写成范围
SELECT * FROM orders WHERE created_at >= '2026-09-01' AND created_at < '2026-09-02';

索引存的是原始值,经过函数转换后无法直接定位。运算同理,id + 1 = 10 不走索引,id = 9 走。

2. 隐式类型转换

列是 VARCHAR,条件里却写了数字:WHERE phone = 13800138000。MySQL 会把列值转成数字比较,相当于对每行套了函数。反过来,数字列传字符串通常没事。这条规则不对称,要记住方向。

3. 前导模糊匹配

LIKE '%abc' 无法使用 B+ 树的有序性。LIKE 'abc%' 可以。业务上确实需要后缀匹配时,考虑倒序列 + 前缀匹配的方案。

4. 违反最左前缀

联合索引 (a, b, c) 只有从 a 开始的连续前缀才能使用。WHERE b = 1 用不上,WHERE a = 1 AND c = 3 只能用 a 部分。设计联合索引时把等值条件排前面,范围条件放最后。

5. OR 连接了非索引列

WHERE a = 1 OR b = 2   -- b 没有索引,整句退化为全表

MySQL 需要对每个条件都能定位,否则只能扫全表。可以拆成 UNION,让两段分别走索引。

6. 优化器判断走索引更慢

回表代价高时,优化器可能直接选择全表扫描——这其实是正确行为。二级索引先定位主键再回表,如果预计命中大量行,随机 IO 不划算。可以:

  • 用覆盖索引(把需要的列加入索引)避免回表;
  • EXPLAIN 看到 Using index 就是覆盖索引生效。

7. 统计信息过期

表频繁大批量写入后,索引的基数统计可能失真,导致优化器选错计划。ANALYZE TABLE 可以刷新。

排查套路

EXPLAIN SELECT ...;
-- 关注:type 至少 range、key 是否命中、rows 预估行数
SHOW INDEX FROM t;   -- 检查基数 Cardinality 是否符合预期

最后提醒:索引不是越多越好。每个索引都会拖慢写入、占用空间,读多写少的表可以多建,写多读少的表要克制。

MySQL索引性能