mysql复合索引严格遵循最左前缀匹配原则:必须从索引最左列开始连续使用,跳过最左列(如只查status或created_at)则索引完全失效;范围查询会截断后续列的索引使用;order by也需符合最左连续且方向一致才可避免filesort。

WHERE 条件没写最左列,索引直接失效
MySQL 的复合索引不是“包含哪些列就都能用”,而是严格依赖定义顺序。比如有索引 INDEX idx_user (city, status, created_at),以下查询能走索引:
WHERE city = 'Beijing'WHERE city = 'Beijing' AND status = 1WHERE city = 'Beijing' AND status = 1 AND created_at > '2025-01-01'
但只要跳过 city,哪怕只查 status 或 created_at,索引就完全不生效——执行计划里 key 字段为空,type 会退化成 ALL 或 index。
范围查询(>、
最左匹配不是“一直往后比”,而是在遇到第一个范围条件时停止向右延伸。例如同样用 idx_user (city, status, created_at):
-
WHERE city = 'Shanghai' AND status IN (1,2) AND created_at = '2025-03-01'→ 全部三列都可用(IN算等值) -
WHERE city = 'Shanghai' AND status > 0 AND created_at = '2025-03-01'→ 只能用到city和status,created_at不参与索引查找(因为status > 0是范围,后续列无法利用有序性) -
WHERE city LIKE 'Bei%'→ 仍算等值前缀匹配,city列有效;但WHERE city LIKE '%jing'就无法使用该列
ORDER BY 和 GROUP BY 也受最左顺序约束
如果 SQL 中有排序或分组,想避免 Using filesort 或 Using temporary,ORDER BY 的字段顺序必须是索引最左连续子集,且方向一致(全 ASC 或全 DESC):
-
WHERE city = 'Guangzhou' ORDER BY status, created_at→ 可走索引排序 -
WHERE city = 'Guangzhou' ORDER BY created_at→ 不行,跳过了status -
WHERE city = 'Guangzhou' ORDER BY status DESC, created_at ASC→ 不行,混合方向,InnoDB 复合索引不支持混合排序优化
注意:SELECT * + ORDER BY 即使走了索引排序,仍可能回表;若只需排序字段,可考虑覆盖索引减少 I/O。
建索引时列顺序不能只看 WHERE,得看查询组合频率和区分度
很多人按“主键→外键→时间”机械排,结果索引利用率低。真实排序逻辑是:
- 高频等值过滤的列放最左(如
tenant_id、user_type) - 高区分度列优先于低区分度列(
user_id比status更适合作左列) - 范围查询列尽量靠右(避免过早截断)
- 排序/分组字段若固定,可合并进索引末尾,但别为了单个
ORDER BY把低频列顶到前面
比如日志表常查 app_id = ? AND level IN (?,?) AND ts BETWEEN ? AND ?,那么 (app_id, level, ts) 比 (ts, app_id, level) 实用得多——后者连 app_id 都没法用上索引。











