我是小耶,干运营半路出家的野生DBA——写功课只是为了我踩过的坑,你们别再踩了!
刚学数据库的时候,我知道联合索引可以给多个字段一起建索引。但我一直搞不懂一个问题:
为什么明明建了
(order_date, user_id),用user_id查的时候,索引还是不走?
后来才知道,联合索引的顺序是有讲究的。顺序错了,等于白建。
一、什么是联合索引?
你把联合索引想象成一个电话本。
电话本的排序规则是:先按姓氏排,再按名字排。比如“王小明”排在“王小刚”前面,因为姓相同,再看名。
联合索引也一样。如果你建了 (a, b),那它会把数据先按 a 排,a 相同再按 b 排。
那么你想查“所有叫小明的人”,能直接翻到那一页吗?不能,因为电话本没有按“名”排,只能一页页翻。这就是为什么 WHERE b = ... 用不上 (a, b) 索引——b 相当于“名”,不是第一排序依据。
结论:查询条件里,必须包含索引的最左列 ,索引才会生效。
这就是 最左前缀原则。
二、最左前缀原则
联合索引 (a, b, c) 可以当成三个索引来用:
- 一个只按
a排的索引 - 一个按
a, b排的索引 - 一个按
a, b, c排的索引
但你不能把它当成 (b, c) 或 (c) 来用。因为跳过了最左列,索引就废了。
规则:
WHERE a = 1✅ 走索引(用了第一列)WHERE a = 1 AND b = 2✅ 走索引(用了前两列)WHERE a = 1 AND b = 2 AND c = 3✅ 走索引(全用)WHERE a = 1 AND c = 3⚠️ 只用到a,c用不上(因为跳过了b)WHERE b = 2❌ 不走索引(没从最左列开始)
三、怎么做?确定联合索引顺序的两条铁律
铁律1:等值查询的列放前面,范围查询的列放后面
什么叫等值?WHERE user_id = 123,就是等值。
什么叫范围?WHERE order_date > '2026-01-01',> 就是范围。
如果你写 WHERE user_id = 123 AND order_date > '2026-01-01',索引应该建 (user_id, order_date)。
为什么?因为 user_id 等值,可以精确定位到某一组数据;然后在这个组里,order_date 是有序的,范围查询只需要沿着这个有序列表往后找。
反过来建 (order_date, user_id),order_date 范围查询后,user_id 的等值就无法在索引里用了,因为后面的列在遇到范围后就失效。
铁律2:如果有 ORDER BY,把排序的列放在最后
假设查询是 WHERE user_id = 123 ORDER BY order_date。
建 (user_id, order_date) 索引,既能快速过滤 user_id,又能让 order_date 天然有序,排序就不用临时做 filesort,快很多。
四、实际案例:优化一条慢查询
场景:订单表几百万行,我要查用户123的“已完成”订单,按订单日期倒序,只要前20条。
原SQL:
SELECT * FROM orders
WHERE user_id = 123 AND status = '已完成'
ORDER BY order_date DESC
LIMIT 20;
原索引:只有 (user_id)。
执行计划:type=ref,rows=5000(这个用户有5000条订单),Extra=Using where; Using filesort(因为 status 没索引,要回表过滤;order_date 没索引,要额外排序)。
优化过程:
- 等值条件有
user_id和status,两个都是等值 → 放前面 - 排序列
order_date→ 放最后 - 希望不回表(覆盖索引)→ 把
SELECT需要的列也加进去
最终建索引:
ALTER TABLE orders ADD INDEX idx_uid_status_date (user_id, status, order_date);
再查:type=ref,rows=86(因为 status 帮索引过滤掉了大部分数据),Extra=Using index condition(索引下推,没有 filesort),速度从几百毫秒降到几毫秒。
价值:同样的查询,加对索引后快了几十倍。
五、你一定会遇到的几个坑
| 错误写法 | 为什么错 | 正确做法 |
|---|---|---|
WHERE a > 1 AND b = 2,建索引 (a, b) |
范围 a 放左边,b 等值失效 |
建索引 (b, a) |
WHERE a = 1 AND c = 3,建索引 (a, b, c) |
跳过 b,c 用不上 |
如果 b 没有条件,可以建 (a, c) 或调整查询 |
ORDER BY b 但索引是 (a, b),且 a 无等值条件 |
不满足最左前缀,ORDER BY 用不上索引 |
建索引 (b) 或给查询加 a 条件 |
六、总结
等值前列,范围后排,排序列收尾,覆盖带上SELECT。
建索引之前,先问自己三个问题:
- WHERE 里哪些是等值?(放最左)
- 有没有范围查询?(放右边)
- 有没有 ORDER BY?(放最后,或考虑覆盖索引)
把这几点搞明白了,你不光能建对索引,还能解释给别人听。
小耶在手,SQL不愁。
你遇到过“建了联合索引还是慢”的情况吗?