联合索引能否使用只取决于最左字段是否在where中等值匹配,如index(a,b,c)中a未出现则全表扫描;order by能否走索引要求排序字段紧接过滤字段且无断层或范围截断;字段顺序应按区分度和查询频率优化,避免NULL。

联合索引到底能不能用上,只看最左字段有没有被等值过滤
mysql 不会因为你写了 b = 2 AND a = 1 就自动“倒着走”索引。它只认最左边那个字段是否出现在查询条件中、且是等值(或 IN)匹配。
只要 a 没出现在 WHERE 条件里,哪怕你建的是 INDEX(a, b, c),这条语句就直接退化为全表扫描:select * FROM t WHERE b = 2 → type=ALL,key=NULL。
- ✅ 正确写法:
WHERE a = 1、WHERE a = 1 AND b > 10、WHERE a IN (1,2,3) AND b = 5 - ❌ 错误写法:
WHERE b = 2、WHERE c = 3、WHERE b = 2 AND c = 4 - ⚠️ 注意:
WHERE a = 1 AND c = 3只能用上a,c完全无效 —— 中间跳过了b,后续字段全部失效
ORDER BY 能不能走索引,取决于排序字段是否在最左前缀连续段内
联合索引不是“包含这些字段就能排序”,而是要求:排序字段必须紧接在过滤字段之后,中间不能断档,也不能被范围查询截断。
比如有 INDEX(user_id, create_time):WHERE user_id = 123 ORDER BY create_time DESC ✅ 无 filesortWHERE user_id > 100 ORDER BY create_time DESC ❌ user_id 是范围,create_time 失效,必 filesort
- ✅ 能避免
using filesort:WHERE a = 1 ORDER BY b、WHERE a = 1 AND b = 2 ORDER BY c - ❌ 必然触发
Using filesort:WHERE a = 1 ORDER BY c(跳过b)、WHERE a = 1 AND b > 10 ORDER BY c(b是范围,c无序) - ? 小技巧:如果常查
WHERE status = ? ORDER BY updated_at,优先建INDEX(status, updated_at),别指望两个单列索引拼起来能排序
字段顺序怎么排?别按字母,要按区分度 + 查询模式
高区分度字段放最左,不是为了“好看”,是为了让 B+ 树第一层就能切掉最多数据。
比如 gender 只有男/女,建 INDEX(gender, age) 相当于先分两桶,再每桶里筛年龄 —— 第一层几乎没过滤能力;反过来建 INDEX(age, gender),先按年龄切成几十段,再每段里筛性别,效率高得多。
- ? 验证选择性:
SELECT count(DISTINCT col)/COUNT(*) FROM table,越接近 1 越好 - ? 查看高频查询:如果 80% 的查询都带
WHERE tenant_id = ?,那tenant_id就该放最左,哪怕它区分度不如id - ? 避免默认值为
NULL:联合索引中任意一列含NULL,该行就不进索引 —— 设计表时尽量用NOT NULL default ''或0
EXPLAIN 显示 type=ALL 或 Using filesort,八成是索引没被“完整驱动”
别急着加索引,先看 EXPLAIN 的 key 和 key_len 字段:
如果 key 是你的联合索引名,但 key_len 很小(比如预期 12,实际只有 4),说明只用了第一个字段;
如果 key=NULL,那连最左字段都没命中,索引等于白建。
- ? 实操建议:用
SHOW INDEX FROM table_name确认索引字段顺序和长度 - ? 测试时别只跑单条 SQL:用真实业务参数,比如
WHERE a = 1 AND b = 2在测试数据里可能命中,但线上a = 1占比 95%,那就几乎不走b - ⚠️ 最容易被忽略的一点:
OR条件会让整个联合索引失效,除非每个OR分支都有独立索引 —— 别试图靠INDEX(a,b)支持WHERE a = 1 OR b = 2