mysql复合索引严格遵循最左匹配原则:等值条件必须从索引最左字段连续出现,范围查询会截断后续字段索引使用;order by需与索引顺序及方向一致才能免排序;避免对索引列使用函数或隐式转换;复合索引字段数建议不超过3个,优先覆盖高频查询组合。

WHERE 条件里字段顺序决定索引能不能用
MySQL 的复合索引不是“包含哪些字段”就行,而是严格按定义顺序匹配 WHERE 中的等值条件。一旦某个字段没出现在最左连续位置(比如跳过第一个字段直接查第二个),后面所有字段索引失效。
常见错误现象:SELECT * FROM orders WHERE status = 'paid' AND user_id = 123,但索引是 INDEX(user_id, status, created_at) —— 这时 status 字段完全走不上索引,因为 user_id 没出现在 WHERE 中。
- 等值条件(
=、IN)必须从左到右连续出现,才能触发最左匹配 - 范围查询(
>、BETWEEN、LIKE 'abc%')会截断后续字段的索引使用,但自身仍可用 - 如果 WHERE 同时有
user_id = 123 AND status IN ('paid','shipped') AND created_at > '2024-01-01',索引(user_id, status, created_at)仍能用前两个字段做等值筛选,created_at仅用于过滤,不参与索引查找
ORDER BY 和 GROUP BY 要和索引顺序对齐才免排序
即使 WHERE 条件完美命中索引,如果 ORDER BY 字段顺序或方向(ASC/DESC)和索引不一致,MySQL 仍要额外排序,性能陡降。
使用场景:分页查询 SELECT * FROM logs WHERE app = 'web' ORDER BY created_at DESC LIMIT 20,若索引是 INDEX(app, created_at),则有效;但如果是 INDEX(app, created_at DESC)(MySQL 8.0+ 支持),且查询也写 ORDER BY created_at DESC,就能跳过 filesort。
- MySQL 5.7 不支持混合 ASC/DESC 索引,
INDEX(a ASC, b DESC)实际被当作INDEX(a, b)处理 -
ORDER BY字段必须是索引最左连续部分,不能跳字段,也不能反向(如索引(a,b),却ORDER BY b,a) - 覆盖索引 + 匹配的
ORDER BY是免排序的关键组合
避免在索引列上做函数或隐式转换
哪怕字段顺序完全匹配,只要在 WHERE 中对索引列用了函数或类型转换,整个索引就作废。
常见错误现象:WHERE DATE(created_at) = '2024-04-01' 或 WHERE user_id = '123'(而 user_id 是 INT 类型)—— MySQL 无法用索引快速定位,只能全表扫描或索引全扫。
- 把函数移到右边:
created_at >= '2024-04-01' AND created_at - 确保参数类型和字段一致:INT 字段就传整数,不要加引号;VARCHAR 字段注意 COLLATION 是否导致隐式转换
-
EXPLAIN中看到type: ALL或key: NULL就要警惕——可能正踩在这个坑里
复合索引字段数不是越多越好,3 个通常是临界点
每多一个字段,索引体积增大、更新开销上升、缓存效率下降。超过 3 个字段的复合索引,除非高频固定组合查询,否则大概率是设计过度。
性能影响:插入/更新变慢(要维护更多 B+ 树节点),Buffer Pool 更容易被大索引挤占,反而拖累整体查询响应。
- 优先保障高频 WHERE + ORDER BY 组合,舍弃低频字段
- 用
SELECT COUNT(DISTINCT concat(a,b,c)) / COUNT(*)估算选择性,低选择性字段(如status只有 3 个值)放后面,甚至不该进复合索引 - 宁可建两个精简索引(如
(a,b)和(a,c)),也不要强行塞成(a,b,c)去覆盖所有情况
最左匹配不是玄学,是 B+ 树结构决定的必然行为。真正难的是在业务迭代中持续识别哪些字段组合正在成为瓶颈,而不是一上来就堆字段。线上 slow_log 和 EXPLAIN 输出里的 key_len 和 rows 才是唯一靠谱的判断依据。











