组合索引必须从最左列开始连续使用,跳过中间列或范围查询后右侧列失效;where缺最左列则索引不生效;order by/group by也需满足最左前缀且方向一致。

组合索引不是“多个字段堆一起就有用”,而是必须从最左列开始连续使用,中间跳过或范围查询后,右侧列就失效了。
WHERE 条件没带最左列,索引直接不走
比如有联合索引 idx_user_status_date(user_id, status, order_date),下面这些查询完全用不上这个索引:
-
WHERE status = 2—— 缺少user_id,B+树根本没法定位起点 -
WHERE order_date > '2024-01-01'—— 连最左列都没提,等同于全表扫描 -
WHERE status = 2 AND order_date = '2024-05-01'—— 同样缺user_id,索引结构无法支撑这种查找
MySQL 不会重排索引列顺序来适配你的 WHERE,它只认定义时的最左前缀。
跳过中间列,右边列自动失效
还是那个 idx_user_status_date(user_id, status, order_date),下面这个查询只会用到 user_id:
SELECT * FROM orders WHERE user_id = 1001 AND order_date = '2024-05-01';
因为 status 被跳过了,order_date 在索引中是第三层排序依据,没有 status 的约束,它的值在 user_id=1001 的子树里是无序的,没法二分查找。
- ✅
WHERE user_id = 1001→ 用第 1 列 - ✅
WHERE user_id = 1001 AND status = 2→ 用前 2 列 - ❌
WHERE user_id = 1001 AND order_date = '2024-05-01'→ 只能用第 1 列,第 3 列无效
范围查询之后的列不再参与索引过滤
一旦某列用了 >、、<code>BETWEEN 或 LIKE 'xxx%'(注意:前导通配符如 LIKE '%xxx' 本身就不走索引),它右边所有列都失去索引加速能力。
SELECT * FROM orders WHERE user_id = 1001 AND status > 1 AND order_date = '2024-05-01';
这里 user_id 和 status 可以走索引定位数据范围,但 order_date = '2024-05-01' 只能在该范围内做逐行过滤,不会用索引跳查。
- 范围列本身可以利用索引(比如快速找到
status > 1的起始位置) - 但它右侧的列,在 B+ 树中已失去有序性保障,MySQL 不再尝试用它们缩小搜索范围
- 如果业务真需要按
order_date精确筛选,考虑把order_date提前——但得权衡其他查询是否受损
ORDER BY 和 GROUP BY 也受最左匹配约束
索引不仅能加速 WHERE,还能避免临时表和 filesort。但前提是排序字段必须构成索引的最左前缀,且顺序一致、方向一致(都是 ASC 或都是 DESC)。
SELECT * FROM orders WHERE user_id = 1001 ORDER BY status, order_date;
这个能用上完整索引,因为 user_id 是等值条件,status 和 order_date 是索引后续连续列,且顺序匹配。
- ✅
ORDER BY user_id, status—— 前缀匹配,有效 - ❌
ORDER BY status, order_date—— 缺user_id,索引无法保证全局有序 - ⚠️
ORDER BY user_id ASC, status DESC—— 混合方向,5.7+ 支持,但旧版本可能退化为 filesort
实际建索引时,得把高频 WHERE 条件列放最左,再把常用于排序/分组的列接在后面——但别硬塞太多列,写多读少的列加进联合索引反而拖慢 INSERT/UPDATE。











