联合索引字段顺序必须遵循b+树最左匹配原则和区分度优先原则:等值查询字段放最左,高区分度(cardinality>0.2)字段优先,如(user_id, shop_id, status);低区分度字段(如status=0/1)放最左会导致索引失效;in可当等值用,但后接范围查询则右侧字段失效;order by/group by需与索引顺序严格一致;主键自动包含,无需重复添加。

联合索引字段顺序不是按查询习惯排,而是按 MySQL 的 B+ 树匹配规则和查询实际路径来定——排错一个位置,整个索引可能就白建。
等值条件字段必须放最左,且高区分度字段优先
MySQL 只能从左到右连续匹配索引字段,中间断掉就停。比如索引是 (status, user_id),但 status 只有 0/1 两个值(区分度极低),那即使加了这个索引,WHERE status = 0 AND user_id = 123 也大概率走全表扫描——因为第一层树节点几乎全是“0”,优化器觉得不如直接扫。
实操建议:
- 用
SHOW INDEX FROM table_name查看Cardinality值,选区分度 > 0.2 的字段打头(如user_id、order_no) - 多个等值条件时,把高频 + 高区分度的放最左,比如
(user_id, shop_id, status)比(status, user_id, shop_id)更可靠 - 别信“我总查 status=0”,低基数字段单独放最左,本质是给优化器埋雷
IN 条件可以当等值用,但后面不能接范围查询
IN 在 MySQL 里被当作多个等值条件处理,所以能继续向右延伸索引匹配。但一旦后面跟了 >、BETWEEN 或 LIKE 'xxx%',右侧所有字段立刻失效。
示例:
- ✅
WHERE user_id IN (1,2,3) AND create_time > '2025-01-01'→ 索引(user_id, create_time)有效 - ❌
WHERE create_time > '2025-01-01' AND status = 1→ 即使索引是(status, create_time),status也用不上 - ⚠️
WHERE user_id IN (1,2,3) AND status IN ('A','B') AND amount > 100→ 索引必须是(user_id, status, amount),且amount是最后一个
ORDER BY 和 GROUP BY 必须和索引顺序完全一致
排序或分组不光看 WHERE,还看是否能复用索引的有序性。如果 ORDER BY a, b,但索引是 (b, a),MySQL 就得额外排序,type 会降为 filesort。
注意点:
-
WHERE a = 1 ORDER BY b→ 索引(a, b)刚好覆盖 -
WHERE a = 1 AND b > 10 ORDER BY c→ 索引(a, b, c)才行;(a, c, b)不行,因为b是范围,c已无法保持有序 - 升序/降序混合(
ORDER BY a ASC, b DESC)在 MySQL 8.0+ 才支持,老版本一律按 ASC 建索引,否则失效
主键不用显式加入联合索引
InnoDB 的二级索引叶子节点自动包含主键值,所以 (a, b) 索引其实已“带”主键。如果查询只涉及 a、b 和主键,就是覆盖索引,不用回表。
常见错误:
- 建
(a, b, id)——id冗余,浪费空间,还拖慢写入 - 以为
SELECT *能靠联合索引避免回表 —— 不行,除非你把所有要查的字段都放进索引里 - 对
TEXT字段建联合索引:它不能做索引列,也不能放在非前导位置参与排序
真正难的不是记口诀,而是每次建索引前,得把对应 SQL 的 WHERE 条件、JOIN ON、ORDER BY 全部摊开,对着 B+ 树一层层推:第一层能不能筛掉 90% 行?第二层还能不能继续剪枝?第三层要不要排序?漏掉任意一环,索引就只是个摆设。











