mysql组合索引需遵循最左前缀原则,高频高区分度字段置左,范围查询后字段失效;order by/group by需与索引顺序一致,覆盖索引可避免回表;in操作宜放索引最右,过多组合索引会加剧写入负担。

WHERE 条件里多个字段,索引到底怎么写才生效
MySQL 不会自动把单列索引拼起来用,哪怕你给 user_id 和 status 各建了一个索引,WHERE user_id = 123 AND status = 'active' 也大概率只走其中一个。真正起效的是「组合索引」,但顺序很关键——最左前缀原则不是口号,是硬约束。
常见错误现象:EXPLAIN 显示 type=ALL 或 key=NULL,明明写了两个条件却没走索引;或者只对 status 做了 IN 查询,结果全表扫。
- 把高频过滤、高区分度字段放最左边(比如
user_id区分度远高于status) - 范围查询(
>、BETWEEN、LIKE 'abc%')后面的字段,索引失效——INDEX(a,b,c)中若a是等值、b是范围,则c不再参与索引查找 - 如果经常查
status+created_at,但created_at总是范围条件,那组合索引应为(status, created_at),而不是反过来
ORDER BY 和 GROUP BY 混在多条件查询里,索引还能不能覆盖
排序和分组不是“额外加的”,它们会直接影响索引是否能被完整利用。MySQL 要么用索引排序(Extra 字段出现 Using filesort 就说明失败),要么被迫临时表+排序,IO 和 CPU 成倍涨。
使用场景:分页查「某类用户最近 10 条订单」,SQL 类似 WHERE category = 'A' ORDER BY created_at DESC LIMIT 10。
- 组合索引必须包含
ORDER BY的字段,且顺序一致(升序/降序也要匹配,MySQL 8.0+ 支持混合方向,但 5.7 及之前要求全同向) -
WHERE等值字段 +ORDER BY字段要连成最左前缀,比如INDEX(category, created_at)可覆盖上面例子;但INDEX(created_at, category)就不行——category不是最左,无法定位范围 - 如果
SELECT还要取order_no、amount等字段,考虑加覆盖索引:在组合索引末尾加上这些字段,避免回表
IN 查询遇上组合索引,为什么有时快有时慢
IN 在组合索引里地位尴尬:它对最左字段是“等值集合”,但对后续字段,MySQL 通常只展开第一个值做索引查找,其余退化为扫描。这不是 bug,是优化器权衡 IO 和 CPU 后的选择。
错误现象:WHERE user_id IN (1,2,3) AND status = 'paid' 走了索引,但换成 user_id IN (1,2,...,500) 就变慢甚至放弃索引;或者 status IN ('paid','refunded') 直接让整个组合索引失效。
- 单个
IN列尽量别放在组合索引中间位置——比如INDEX(user_id, status, created_at)中,user_id IN (...)可接受,但status IN (...)会让created_at失效 - 如果必须多值筛选,优先把
IN字段放索引最右(如INDEX(user_id, created_at, status)),至少保证前两列能快速定位数据块 - 超过几十个值的
IN,考虑改用临时表 JOIN,比让优化器硬扛更可控
联合索引太多反而拖慢写入,哪些字段真该进组合索引
每多一个组合索引,INSERT/UPDATE/DELETE 就得多维护一份 B+ 树。线上表如果写入频繁、字段又多,盲目堆索引只会让延迟飙升、磁盘 IO 拉满。
性能影响明显的情况:日志类表每秒写入上千行,同时建了 (a,b)、(a,c)、(b,c) 三个两列索引,写放大直接翻三倍。
- 先看
WHERE的实际谓词组合——用pt-query-digest或慢查日志统计,只对真实高频组合建索引,别按“可能用到”预设 - 区分度低的字段(如
gender、is_deleted)慎入组合索引,除非它永远出现在最左且后面跟着强过滤字段 - 已有单列索引的字段,别急着塞进新组合索引——确认旧索引真的没被用了(
SHOW INDEX+information_schema.STATISTICS查SEQ_IN_INDEX),再删
复杂点在于:同一个字段在不同查询中角色不同——可能是过滤条件,也可能是排序依据,还可能只是 SELECT 列。这时候得拆开看执行计划,而不是靠经验拍顺序。











