联合索引(a,b,c)仅当where条件从a开始连续匹配时才生效:a=1✅、a=1 and b=2✅、a=1 and b=2 and c=3✅;b=2❌、a=1 and c=3❌、a>10 and b=5中b失效。

看查询条件是否满足最左前缀匹配
联合索引 (a, b, c) 能被用上,只取决于 WHERE 条件是否从 a 开始、连续使用字段。不是“WHERE 里有 a、b、c 就行”,而是必须 a = ? 或 a IN (?, ?) 打头,后面才能接 b = ?,再接 c = ?。
常见错误现象:
-
WHERE b = 1 AND c = 2→ 完全不走索引,type=ALL -
WHERE a = 1 AND c = 3→ 只用上a,c失效(b缺失导致断层) -
WHERE a > 10 AND b = 5→a用上,b无法用于查找(范围查询后列失效)
实操建议:
- 用
EXPLAIN看key_len:值越小说明用上的索引列越少,比如key_len = 4(假设a是 INT)就代表只用了第一列 - 别依赖优化器重排 WHERE 顺序——它只在无函数、无计算、同级 AND 的情况下才可能调整,自己写对更稳
查字段区分度,高区分度字段优先放左
区分度低的字段(如 del_flag、gender)放最左,会导致 B+ 树第一层就只有两三个分支,后续字段再精细也白搭。索引效率取决于“先切多细”,而不是“总共切几刀”。
验证方法:
- 执行
SELECT COUNT(DISTINCT col) / COUNT(*) FROM table,结果越接近1,该列越适合作为首列 - 查
SHOW INDEX FROM table,看Cardinality值,越高越好
典型反例:
-
INDEX(del_flag, user_id)→del_flag只有 0/1,Cardinality ≈ 2,索引几乎退化为全表扫描 - 正确写法应是
INDEX(user_id, del_flag),先按唯一/高区分字段定位,再筛状态
对齐 ORDER BY 和 GROUP BY 的字段顺序
如果查询带排序或分组,ORDER BY a, b 要想免去 Using filesort,索引必须是 (a, b) 或 (a, b, c),且顺序、方向严格一致。MySQL 8.0+ 支持降序索引,旧版本只能靠 ASC 顺序硬扛。
关键限制:
-
WHERE a = 1 ORDER BY b DESC, c ASC→ 索引(a, b, c)无法完全覆盖,因方向不一致(8.0+ 可建(a, b DESC, c ASC)) -
WHERE a > 10 ORDER BY b→ 即使索引是(a, b),a是范围查询,b的有序性在结果集中已丢失,仍会Using filesort
实操建议:
- 用
EXPLAIN看Extra字段:出现Using filesort就说明排序没走索引 - 避免在
ORDER BY中混用 ASC/DESC(除非你明确用了 8.0+ 的降序索引)
结合高频查询模式定顺序,不是按字母或建表顺序
字段顺序不是技术指标堆出来的,而是由业务查询决定的。比如常查 WHERE tenant_id = ? AND user_id = ? ORDER BY create_time DESC,那索引就该是 (tenant_id, user_id, create_time),而不是把 create_time 放最前——因为没 tenant_id 就根本不会发起这个查询。
容易踩的坑:
- 把“看起来重要”的字段(如主键、时间戳)默认放前面,但实际查询从不单独用它
- 为兼容多种 WHERE 组合强行塞字段进索引,结果每个查询都只用前 1–2 列,其余冗余还拖慢写入
- 忽略
IN的特殊性:a IN (1,2,3) AND b = 5算等值匹配,能走(a, b);但a > 1 AND b = 5中b就不能用于查找
真正要盯住的,是慢查询日志里反复出现的 WHERE 模式,不是表结构本身。











