联合索引字段应按“高选择性+查询模式”排序,遵循最左前缀原则;等值条件优先于范围条件;冗余单列索引可删除;group by/distinct需字段顺序完全匹配索引顺序才能加速。
联合索引字段顺序怎么排才有效
字段顺序直接决定索引能否被 where 条件命中,不是按“常用程度”或“字段长短”排,而是按“过滤性 + 查询模式”定。
- 最左前缀原则是硬约束:查询必须从索引第一个字段开始连续匹配,
INDEX(a, b, c)能用在WHERE a = 1 AND b = 2,但不能用在WHERE b = 2 AND c = 3 - 高选择性字段(如
user_id)放左边,比低选择性字段(如status,只有 0/1)更利于快速缩小扫描范围 - 如果常有
ORDER BY a, b,且a已在WHERE中,那a, b顺序天然匹配排序需求,避免额外文件排序 - 注意等值条件优先于范围条件:在
WHERE a = 1 AND b > 10 AND c = 5中,c实际无法走索引,因为b > 10是范围查询,会截断最左前缀
什么时候该删掉单列索引保留联合索引
冗余索引不仅浪费磁盘和写性能,还会拖慢优化器的执行计划选择速度。
- 如果已有
INDEX(a, b, c),再建INDEX(a)或INDEX(a, b)就是冗余——前者完全被覆盖,后者是前者的前缀 - 但
INDEX(b)不能被INDEX(a, b, c)替代,除非你从不单独查b - 检查方式:用
SHOW INDEX FROM table_name看索引字段;再用EXPLAIN SELECT ...验证实际走了哪个索引 - 线上删索引前务必确认慢查询日志里没有依赖该单列索引的语句,尤其注意 ORM 自动生成的 count 或 exists 查询
联合索引能加速 GROUP BY 和 DISTINCT 吗
能,但前提是字段顺序和 GROUP BY 列完全一致,且没有中间出现范围条件或函数包裹。
-
SELECT a, b, COUNT(*) FROM t GROUP BY a, b可以直用INDEX(a, b)完成分组,避免临时表和 filesort - 但如果写成
GROUP BY b, a,即使索引是(a, b),也无法利用——顺序不匹配 -
DISTINCT a, b同理,但注意:DISTINCT在 MySQL 8.0+ 会尝试用松散索引扫描(loose index scan),对INDEX(a, b)有效;而DISTINCT b, a就不行 - 带聚合函数的场景(如
MAX(c))要求索引包含c字段,否则仍需回表
ALTER TABLE ADD INDEX 的坑和替代方案
直接在线加联合索引在大表上可能锁表、耗时长,尤其 MySQL 5.6 以前默认阻塞 DML。
- MySQL 5.7+ 默认支持
ALGORITHM=INPLACE,但若字段含TEXT/BLOB或启用了innodb_file_per_table=OFF,仍可能退化为 copy 方式 - 别在高峰期跑
ALTER TABLE ... ADD INDEX,先用pt-online-schema-change或gh-ost做无锁变更 - 创建前用
SELECT COUNT(DISTINCT CONCAT(a,b)) * 100.0 / COUNT(*) FROM t估算组合选择性,低于 1% 就要怀疑是否真有必要建这个联合索引 - 测试阶段记得关掉
innodb_stats_on_metadata,避免SHOW INDEX触发统计信息重采样,干扰执行计划判断
联合索引不是堆字段就能生效,字段顺序、查询写法、MySQL 版本、甚至字符集排序规则都会悄悄改变它的行为。上线前拿真实慢查询 EXPLAIN 一遍,比背原理管用。










