联合索引第一个字段用范围查询时索引大概率失效,因最左前缀原则中断且优化器预估回表代价高而弃用;应将高选择性等值字段前置、范围字段置后,并结合覆盖索引与ICP优化。

联合索引里第一个字段用范围查询,索引大概率失效
MySQL 对联合索引的使用严格遵循最左前缀原则,但更关键的是:一旦某个字段在 WHERE 中使用了范围操作(如 >、<、BETWEEN、LIKE 'abc%'),它**右侧所有字段就无法用于索引查找**(仅可能参与 ICP 过滤)。尤其当范围条件出现在联合索引**第一位**时,优化器往往直接放弃走该索引——因为预估回表代价太高,不如全表扫描。
- 典型失效 SQL:
SELECT * FROM orders WHERE create_time > '2023-01-01' AND user_id = 100;(假设索引是(create_time, user_id)) -
EXPLAIN中type常为ALL或index,key为NULL - 即使强制
FORCE INDEX,实际执行时间也可能比全表扫描还慢
把范围字段挪到联合索引末尾,才能保住左侧等值字段的查找能力
核心目标是让高选择性、常用于等值匹配的字段(如 user_id、status)排在前面,范围字段(如 create_time、amount)放在最后。这样 MySQL 可以先用等值条件快速定位数据块,再在该块内做范围筛选。
- 优化后索引示例:
ALTER TABLE orders ADD INDEX idx_user_time (user_id, create_time); - 对应有效查询:
SELECT * FROM orders WHERE user_id = 100 AND create_time BETWEEN '2023-01-01' AND '2023-12-31'; -
EXPLAIN中type = ref,key_len显示只用了user_id部分(4 字节),Extra可能含Using index condition(ICP 生效)
排序需求强时,范围字段后还可接 ORDER BY 字段(需谨慎)
如果查询带 ORDER BY 且希望避免 Using filesort,可以把排序字段放在范围字段之后——但这仅在范围条件结果集较小时才真正生效。MySQL 无法跳过范围字段去利用后续字段做排序。
- 可行场景(小范围结果):
SELECT * FROM orders WHERE user_id = 100 AND create_time > '2023-12-01' ORDER BY amount DESC;→ 索引(user_id, create_time, amount) - 不可行场景:
WHERE create_time > '2020-01-01' ORDER BY user_id→ 即使索引是(create_time, user_id),也无法避免排序 - 验证方式:检查
EXPLAIN的Extra是否出现Using filesort
别忽略 ICP 和覆盖索引的协同价值
把范围字段放后面只是第一步。若查询只涉及索引列,搭配覆盖索引可彻底避免回表;而 ICP(MySQL 5.6+)能让存储引擎在读取索引页时就过滤掉不满足范围条件的记录,减少回表次数。
- 覆盖查询示例:
SELECT user_id, create_time FROM orders WHERE user_id = 100 AND create_time > '2023-01-01';→ 索引(user_id, create_time)完全覆盖 - ICP 示例:
SELECT * FROM orders WHERE user_id = 100 AND create_time > '2023-01-01' AND status = 'paid';→ 索引(user_id, create_time, status),status在索引层被 ICP 过滤 - 注意:ICP 不改变
rows预估值,但实际 IO 和 CPU 开销会下降
真实业务中,user_id + 时间范围是最常见组合,但很多人建反了顺序。范围字段位置不是“能不能加”,而是“加在哪才能让前面的等值条件真正生效”——这点一旦搞错,索引就形同虚设。


















