联合索引的顺序:写错等于白建(最左前缀+范围条件+覆盖索引详解)
我是小耶干运营半路出家的野生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⚠️ 只用到ac用不上因为跳过了bWHERE 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条。原SQLSELECT * FROM orders WHERE user_id 123 AND status 已完成 ORDER BY order_date DESC LIMIT 20;原索引只有(user_id)。执行计划typerefrows5000这个用户有5000条订单ExtraUsing 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);再查typerefrows86因为status帮索引过滤掉了大部分数据ExtraUsing index condition索引下推没有filesort速度从几百毫秒降到几毫秒。价值同样的查询加对索引后快了几十倍。五、你一定会遇到的几个坑错误写法为什么错正确做法WHERE a 1 AND b 2建索引(a, b)范围a放左边b等值失效建索引(b, a)WHERE a 1 AND c 3建索引(a, b, c)跳过bc用不上如果b没有条件可以建(a, c)或调整查询ORDER BY b但索引是(a, b)且a无等值条件不满足最左前缀ORDER BY用不上索引建索引(b)或给查询加a条件六、总结等值前列范围后排排序列收尾覆盖带上SELECT。建索引之前先问自己三个问题WHERE 里哪些是等值放最左有没有范围查询放右边有没有 ORDER BY放最后或考虑覆盖索引把这几点搞明白了你不光能建对索引还能解释给别人听。小耶在手SQL不愁。你遇到过“建了联合索引还是慢”的情况吗