在数据库性能优化中,建了索引却不生效是最常见、也最让人困惑的问题之一。很多开发者以为有索引就一定会用,但实际上数据库优化器是基于成本计算来决定执行计划的,并不会强制使用索引。本文将系统分析SQL不走索引的常见原因,并给出对应优化思路。
索引失效的本质:优化器的成本选择
数据库优化器会综合考虑数据量、过滤条件、统计信息等因素,选择成本最低的执行方式。如果它认为全表扫描比走索引更快,就会直接放弃索引。
换句话说:不走索引不一定有问题,而是有更优的选择。
常见导致SQL不走索引的原因
1. 对索引列进行了函数或计算操作
例如:
SELECT * FROM user WHERE YEAR(create_time) = 2026;
这类写法会导致索引失效,因为数据库需要逐行计算表达式。
原因:破坏了可索引性(Sargability)
优化写法:
WHERE create_time >= '2026-01-01' AND create_time < '2027-01-01'
2. 使用了模糊查询(LIKE '%xxx')
WHERE name LIKE '%abc'
前缀不确定,索引无法定位起始位置。
常见问题:
- LIKE 'abc%' 可用索引
- LIKE '%abc' 索引失效
3. 联合索引未命中最左前缀原则
例如索引:
INDEX(user_id, create_time)
但SQL:
WHERE create_time = '2026-01-01'
不包含 user_id,索引可能无法使用。
4. 条件选择性太低(低区分度)
比如:
WHERE gender = 'male'
如果90%数据都是 male,数据库会认为:走索引不如直接全表扫描更快。
5. 返回数据量过大(回表成本高)
SELECT * FROM orders WHERE status = 1;
如果返回大量数据:
- 使用索引 → 多次回表
- 全表扫描 → 一次读取
优化器会选择扫描。
6. 数据类型不匹配或隐式转换
WHERE user_id = '123' -- 字符串 vs int
数据库会进行类型转换 → 索引失效。
7. 统计信息过期(Statistics失效)
数据库依赖统计信息决定执行计划,如果统计信息不准:
- 误判数据分布
- 错误选择全表扫描
8. 索引设计不合理
常见问题:
- 索引列顺序错误
- 缺少高频查询字段
- 低选择性字段建索引
9. 小表或数据量太少
当表只有几百行,全表扫描更快,索引反而是负担。
10. 范围查询导致索引截断
WHERE user_id = 1 AND create_time > '2026-01-01'
一旦遇到范围条件(>、<、BETWEEN),后续索引列可能无法继续利用。
如何判断SQL是否走索引?
最直接方法:
EXPLAIN SELECT ...
重点看:
- type(ALL = 全表扫描)
- key(使用的索引)
- rows(扫描行数)
优化建议(实战总结)
1. 写可走索引的SQL
- 避免函数操作
- 避免隐式转换
- 使用范围替代函数
2. 合理设计索引
- 高选择性字段优先
- 符合最左匹配原则
- 建覆盖索引(避免回表)
3. 控制返回字段
SELECT id, name -- 不要 SELECT *
4. 定期维护数据库
- 更新统计信息
- 重建索引(减少碎片)
5. 用执行计划说话
不要凭感觉优化,一切以 EXPLAIN 为准。
总结
SQL不走索引,通常不是索引没建,而是以下几个核心问题:
- SQL写法不合理(函数、模糊查询)
- 索引设计不匹配(顺序、选择性)
- 数据分布问题(低区分度)
- 优化器认为全表扫描更快
索引是否生效,取决于成本,而不是是否存在。