因为联合索引遵循最左前缀匹配,且范围查询(如>、<)会使右侧字段无法用于索引查找,只能过滤;故WHERE a = ? AND b > ? AND c > ?在(a,b,c)索引上仅a、b生效,c失效。

为什么 WHERE a = ? AND b > ? AND c > ? 用不上 (a, b, c) 联合索引的全部字段?
因为 MySQL 的联合索引是「最左前缀匹配」,但更关键的是:一旦遇到范围查询(>、<、BETWEEN、LIKE 前缀通配除外),其右侧所有字段就**无法用于索引查找(ref/eq_ref)**,只能做排序或过滤(Extra: Using where)。
比如 WHERE a = 1 AND b > 10 AND c > 20 在索引 (a, b, c) 上:
a 精确匹配 → 定位到索引某一段;
b > 10 → 在该段内向右扫描;
c > 20 → 此时已脱离“查找”阶段,MySQL 只能逐行读取 c 值判断,无法跳过不满足的记录。
- 等值字段(
=)应放最左,越多越好 - 范围字段只保留一个,且尽量放在等值字段之后、其他字段之前
- 如果必须多个范围条件,优先把过滤性更强(区分度更高)的放前面,减少扫描行数
如何判断哪个字段过滤性更强?别猜,查 SELECT COUNT(DISTINCT col)/COUNT(*)
区分度(selectivity)≈ 唯一值占比。值越接近 1,字段越适合作为索引前导或范围字段。
例如:
SELECT COUNT(DISTINCT status)/COUNT(*) AS s FROM orders; SELECT COUNT(DISTINCT created_at)/COUNT(*) AS t FROM orders;
若 status 只有 3 个枚举值,结果约 0.003;而 created_at 是毫秒时间戳,结果接近 1 —— 那么在 WHERE status = ? AND created_at > ? 场景下,即使 status 是等值,也不该把它放索引最左,除非你确定它能先筛掉 95%+ 数据。
- 高区分度字段(如
user_id、order_no、精确时间戳)更适合做等值匹配位 - 低区分度字段(如
status、is_deleted)放在索引右侧,或干脆不入联合索引(改用单独索引 + 条件下推) - 注意:统计信息可能过期,执行
ANALYZE TABLE orders再查
ORDER BY 和范围查询冲突时,索引字段顺序怎么取舍?
当 SQL 同时含范围条件和 ORDER BY,比如 WHERE a = ? AND b > ? ORDER BY c DESC,索引设计要兼顾「查找」和「避免 filesort」。
此时最优索引通常是 (a, b, c) ——
a 等值定位 → b > 扫描 → 扫描出的记录天然按 c 排好序(B+ 树叶子节点有序)。
- 如果写成
(a, c, b):能用上a,但b >变成范围后,c就失效了,ORDER BY c必触发Using filesort - 如果还有
LIMIT(如分页),索引覆盖排序能极大减少临时内存/磁盘开销 - 注意:
ASC/DESC在 MySQL 8.0+ 支持混合方向索引,但 5.7 及以前所有字段默认 ASC,ORDER BY c DESC配(a,b,c)仍有效(因 B+ 树可双向遍历)
实战检查:用 EXPLAIN 看 key_len 和 Extra 到底用了索引多少
别只看 type=range 就以为优化好了。重点看两处:
-
key_len:值越大,说明用到的索引字段越多。比如索引(a,b,c)各为 INT,理论最大key_len=12;若实际只有8,说明只用到了前两个字段 -
Extra中是否出现:
–Using index:覆盖索引,没回表
–Using where:索引没覆盖 WHERE 全部条件,需回表过滤
–Using filesort:排序没走索引 - 执行
EXPLAIN FORMAT=JSON可看used_key_parts,明确列出哪些字段被索引使用
真正难的不是建索引,是理解数据分布、查询模式和索引底层如何交互。很多线上慢查,调换两个字段顺序,key_len 从 4 跳到 12,QPS 翻倍——但前提是,你知道该盯住哪一行 EXPLAIN 输出。


















