联合索引中范围查询“切断”右侧列,因B+树按字典序嵌套排序,仅前导列全等值时后续列有序;一旦某列使用>、

联合索引中范围查询为什么“切断”右侧列
因为联合索引的 B+ 树是严格按字典序嵌套排序的:先排第一列,该列值相同时再排第二列,以此类推。这种有序性只在“前导列全部等值”的路径上成立。一旦某列用了 >、、BETWEEN 或 LIKE 'abc%',MySQL 就无法精确定位到一个连续子树,而必须扫描多个分支——这些分支里,后续列的值天然不再全局有序。
比如联合索引 (sn, name, age),执行 sn = 'S001' AND name > 'A' AND age = 25 时:
-
sn = 'S001'能定位到一个子树 -
name > 'A'在该子树内横向遍历多个name值节点 - 但每个匹配的
name下,age是各自独立排序的,整体无序 → 优化器无法跳转到某个age = 25的位置,只能回表后逐行过滤
IN 不截断但 != 会彻底失效
IN 看似是范围,实际被优化器拆成多个等值路径,每条路径仍保持后续列有序,所以不触发截断;而 !=、NOT IN、IS NOT NULL 通常被视作全范围扫描,右侧列直接丢弃。
示例对比:
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
-
sn = 'S001' AND name IN ('张三','李四') AND age = 25:能用满三列 -
sn = 'S001' AND name != '张三' AND age = 25:age完全不参与索引查找,Extra 中大概率出现Using where且无Using index
验证右侧列是否真失效,别只看 key_len
key_len 只反映“索引用了多少字节”,不是“用了哪些列”。更可靠的方式是结合 EXPLAIN FORMAT=TREE(MySQL 8.0+)和 Extra 字段:
- 查
key_len:若索引(a,b,c)各占 4 字节,key_len=8表示只用到了 a 和 b,c 没参与查找 - 看
Extra:出现Using where但没Using index,说明 c 是在回表后过滤的 - 用
EXPLAIN FORMAT=TREE:可直观看到range access on ...停在哪一层,比key_len更准
把范围列放右边也不总管用
调换索引顺序(如把 created_at 放最后)看似合理,但真实效果取决于业务分布:
- 如果高频查询是
status IN (1,2) AND created_at > '2026-01-01',建INDEX (status, created_at)更稳 —— 因为IN不截断,created_at至少还能用上 - 但如果
created_at总是窄区间(如单日),它实际接近等值,放中间也未必差 - 最麻烦的是低区分度字段(如
gender)+ 范围组合,此时无论怎么调序,优化器都可能放弃索引
真正容易被忽略的是:即使 key 显示命中了索引,只要 rows 接近总行数,且 Extra 里没有 Using index,就说明右侧列根本没起到过滤作用,只是白走了一趟索引结构。










