设计mysql复合索引应按查询逻辑和b+树特性排序,优先等值条件,再范围条件,最后排序字段;避免冗余单列索引,控制字段数在3–4个以内。

设计MySQL复合索引,关键不是堆字段,而是按查询逻辑和B+树特性来排顺序。核心目标是让索引真正被用上,而不是建了却无效。
先看查询条件,分清等值、范围和排序
复合索引能否生效,取决于WHERE、ORDER BY、GROUP BY中实际出现的列及其操作类型:
- 等值条件(=、IN):过滤性强,应优先放在索引左侧;
- 范围条件(>、:会截断右侧列的索引使用,必须靠右放;
- 排序或分组字段:若查询含 ORDER BY a,b,且a已在索引最左,b紧随其后,就能避免额外排序。
按选择性从高到低排字段顺序
选择性 = 不同值数量 / 总行数。值越接近1,区分度越高,越适合作为索引首列:
- 用户ID、手机号、邮箱等高选择性字段,适合放最左;
- 性别、状态、类型等低选择性字段(如只有2–5个取值),尽量不单独建索引,若必须进复合索引,应放在右侧;
- 示例:user表中,email选择性约0.98,gender约0.02,则 (email, gender) 比 (gender, email) 高效得多。
遵循最左前缀,兼顾覆盖需求
索引 (a,b,c) 只能加速以下查询:
- WHERE a = ?
- WHERE a = ? AND b = ?
- WHERE a = ? AND b = ? AND c = ?
- WHERE a = ? ORDER BY b, c(满足排序有序性)
若查询只用到 b 或 c,该索引完全失效。另外,如果 SELECT 的字段都包含在索引中(如索引是 (user_id, name, city),查询是 SELECT name, city FROM t WHERE user_id = 123),就构成覆盖索引,无需回表,性能更优。
避免常见错误组合
这些设计看似合理,实则容易浪费索引资源:
- 把范围列放在中间:如 (status, create_time, user_id),但 status 是低选择性且常为 =,create_time 却是 >,结果 user_id 无法走索引;
- 重复建单列索引后再建复合索引:如已有 INDEX(a) 和 INDEX(b),再建 INDEX(a,b),其中 INDEX(a) 实际可被替代,反而增加写开销;
- 字段过多:超过3–4列的复合索引维护成本陡增,且命中率未必提升,优先精简高频路径。











