联合索引列顺序直接影响WHERE等值条件缺失、范围查询截断、ORDER BY不匹配时的索引使用:如(a,b,c)索引支持a、a+b、a+b+c查询,但不支持b、a+c或b+c;范围查询(>、BETWEEN等)后列仅能ICP过滤,无法参与定位。

联合索引列顺序直接影响哪些查询能走索引
MySQL 的联合索引不是“多个字段随便排”,而是按定义顺序构建 B+ 树:先排第一列,相同时再排第二列,以此类推。这意味着 (a, b, c) 索引天然支持 WHERE a = ?、WHERE a = ? AND b = ?、WHERE a = ? AND b = ? AND c = ?,但不支持 WHERE b = ? 或 WHERE a = ? AND c = ?(中间缺 b)。
设计顺序前必须明确:哪些查询最频繁?哪些字段过滤性最强?哪些字段常用于排序或分组?
- 高选择性字段(如
user_id、order_no)放左边,能更快缩小扫描范围 - 等值查询字段(
=、IN)优先于范围查询字段(>、BETWEEN、LIKE 'abc%')——因为范围会中断后续列的索引定位能力 - 排序字段(
ORDER BY)和分组字段(GROUP BY)应尽可能与索引最左连续段一致,避免Using filesort - 如果某列几乎总出现在
WHERE中且是等值条件,它大概率该放最左;如果只在SELECT列表里出现,它适合放最后(覆盖索引场景)
WHERE 条件含范围查询时,后续列只能用于 ICP 过滤
当联合索引是 (status, create_time, user_id),而查询写成 WHERE status = 1 AND create_time > '2025-01-01' AND user_id = 123,MySQL 实际只用到了前两列进行数据定位,user_id = 123 不参与查找,仅在从索引叶子节点读出的行中做内存过滤(即 Index Condition Pushdown)。这比全表扫描快,但远不如三列都参与定位高效。
- 范围查询包括:
>、<、>=、<=、BETWEEN、LIKE 'prefix%'(注意:LIKE '%suffix'不走索引) -
!=和<>也被视为范围操作,同样会截断匹配链 - 如果业务上
create_time常用范围,又需要user_id高效过滤,考虑把user_id提到create_time左边(前提是它也满足等值前置条件)
ORDER BY 顺序必须严格匹配索引最左连续段
索引 (a, b, c) 可以加速 ORDER BY a, b 或 ORDER BY a,但无法加速 ORDER BY b, c 或 ORDER BY a, c(跳过 b)或 ORDER BY c, b, a(顺序颠倒)。
- 复合排序方向要一致:
ORDER BY a ASC, b DESC无法利用(a, b)索引(除非 MySQL 8.0+ 且索引显式声明了混合方向) - 如果查询既有
WHERE a = ?又有ORDER BY b, c,而索引是(a, b, c),就能同时满足过滤和排序;但如果写成ORDER BY c, b,就会触发Using filesort - 不要为了排序强行把低选择性字段(如
status)放最左——若它导致大量等值结果,后续排序字段反而失去意义
别忽略 WHERE 条件顺序和执行计划验证
SQL 中 WHERE 子句的书写顺序不影响优化器是否走索引(MySQL 5.7+ 已足够智能),但 EXPLAIN 结果里的 key 和 key_len 才是真实依据。比如 key_len = 5 表示只用了索引前两字节(假设第一列是 TINYINT),说明后续列没被命中。
- 务必用
EXPLAIN FORMAT=TRADITIONAL查看实际使用的索引列数,而不是只看possible_keys - 注意
type字段:ref表示等值匹配,range表示范围扫描,index表示全索引扫描(可能低效) - 测试时用真实数据量,小表即使没索引也可能比走索引快,容易误判
(tenant_id, status, created_at),今天因 tenant_id 被下掉,就彻底失效了。


















