数据库索引为什么没被用上
示例文章,用于演示分页与列表排版,请替换成你自己的内容。
建了索引却走了全表扫描,是最常见的一类性能问题。原因通常不在索引本身,而在于查询写法让优化器判断「用索引更慢」。
最常见的原因:列上套了函数
-- 索引失效
SELECT * FROM orders WHERE DATE(created_at) = '2026-06-25';
-- 走索引
SELECT * FROM orders
WHERE created_at >= '2026-06-25' AND created_at < '2026-06-26';
第一条在列上套了函数,优化器没法用索引里的有序性去定位范围,只能逐行算。第二条把函数挪到了常量一侧,范围查询可以直接用上索引。
同样的道理适用于隐式类型转换:字符串列拿去和数字比较,等于给列套了一次转换函数。
联合索引的最左前缀
联合索引 (a, b, c) 相当于按 a、再按 b、再按 c 排序。能用的查询必须从最左列开始连续匹配:
| 查询条件 | 能否用上 |
|---|---|
a = ? | 能 |
a = ? AND b = ? | 能 |
b = ? | 不能 |
a = ? AND c = ? | 只能用到 a |
a = ? AND c = ? 这种情况,索引只能把范围缩到 a 相同的那一段,c 的条件要在回表后逐行判断。
选择性太低时优化器会放弃
如果某个条件能匹配表中大部分行,走索引要先扫索引再大量回表,反而比顺序全表扫描更慢。优化器基于统计信息估算代价,判断不划算就不用索引——这是正确的行为。
这时该考虑的不是「怎么逼它用索引」,而是这个查询本身是否需要换一种设计。
怎么确认
不要猜,让数据库告诉你执行计划。各个数据库的命令不同(EXPLAIN、EXPLAIN ANALYZE 等),关注两件事:实际走了哪个索引,以及估算行数与实际行数差多少。统计信息过期是另一个常见坑。