← 返回题目列表

B+ 树联合索引为什么遵循最左前缀原则?

高频 中等 第 8 / 25 题 更新于 2026/08/03
B+树联合索引最左前缀数据库索引

简化版

B+ 树联合索引会按多个列从左到右拼成复合 key 排序,例如 (a,b,c) 先按 a 排,再按 b 排,最后按 c 排。因此查询只有从最左列开始约束,才能在树上定位到连续范围;跳过 a 直接查 b,全局上并不是有序的。

详细版

联合索引 (a,b,c) 在 B+ 树里不是三棵树,而是一棵按复合 key 排序的树。排序规则类似字典序:

  1. 先比较 a
  2. a 相同再比较 b
  3. b 相同再比较 c

所以:

  • where a=1 可以用;
  • where a=1 and b=2 可以用;
  • where a=1 and b=2 and c=3 可以用;
  • where b=2 通常不能高效利用 (a,b,c) 的有序定位;
  • where a=1 and c=3 可以先用 a 定位范围,但 c 不能像连续前缀那样缩小树搜索范围。

范围条件也会截断后续列的有序利用,例如 a=1 and b>10 and c=3,通常能用到 a,b 定位范围,但 c 很难继续用于精确缩小 B+ 树扫描区间。

完整版教学

一、联合索引在 B+ 树里到底长什么样

联合索引不是给每个列各建一层树,而是把多个列组合成一个复合 key。对于 (a,b,c),叶子页中的索引项按 (a,b,c) 的字典序排列。这个排序规则决定了查询能不能变成一段连续扫描。

例如索引项可能是:

(1,1,5)
(1,2,1)
(1,2,9)
(2,1,1)
(2,2,3)
(3,1,8)

你会发现所有 a=1 的项连续,所有 a=1,b=2 的项也连续;但所有 b=2 的项不连续,它们被不同 a 分散隔开。这就是最左前缀原则的直观来源。

记忆钩子:联合索引像英文字典,先按第 1 个字母排;不知道第 1 个字母时,直接按第 2 个字母找就会散落全书。

二、为什么跳过最左列会失去连续范围

B+ 树查找依赖 key 的全局有序性。若查询 where b=2,在 (a,b,c) 索引中,b=2 的记录会分布在 a=1a=2a=3 等多个区域。树没有办法通过一次从根到叶的定位找到一段完整连续范围,只能扫描大量索引项再过滤。

用一个小表看得更清楚:

查询条件(a,b,c) 中是否连续能否高效定位
a=1连续可以
a=1 and b=2连续可以
b=2不连续通常不行
a=1 and c=3a=1 连续,c 在范围内过滤部分可用

所以最左前缀不是数据库随便定的规则,而是复合 key 排序方式自然带来的限制。

三、范围条件为什么会截断后续列

如果条件是 a=1 and b>10 and c=3,B+ 树可以定位到 a=1,b>10 的起点,然后沿叶子链表扫描。但在 b>10 这个范围内,c 的值不是全局连续排列的,因为排序优先级仍然是先 b 后 c。不同 b 值下面各自有 c 的局部顺序,整体不能直接按 c=3 再缩成一段。

(1,11,1)
(1,11,3)
(1,12,2)
(1,12,3)
(1,13,1)
(1,13,3)

c=3 分散在多个 b 组里,不能形成单个连续区间。因此范围列之后的列通常只能用于索引条件下推或过滤,不能继续缩小树定位范围。

四、最左前缀和索引下推不是一回事

有些数据库可以在索引扫描过程中做 Index Condition Pushdown,把后续列条件尽早在存储引擎层过滤。比如 (a,b,c)a=1 and b>10 and c=3c=3 可能不能继续缩小扫描范围,但如果 c 在索引里,扫描索引项时可以先过滤 c,减少回表。

概念作用是否改变扫描范围
最左前缀决定能定位到哪段索引范围
索引下推在扫描过程中提前过滤通常否
覆盖索引查询列都在索引里,避免回表否,但减少 I/O

面试时把这三件事分开讲,会比只背口诀清楚得多。

五、联合索引列顺序怎么设计

联合索引列顺序要服务查询模式。常见原则是把高频等值条件放前面,把范围条件放后面,把排序和分组需要的列纳入连续前缀。但选择性不是唯一指标,如果某列虽然选择性高却不常作为前缀查询,也未必适合放最前。

例如常见查询是:

where tenant_id = ? and status = ? and created_at between ? and ?
order by created_at

索引 (tenant_id, status, created_at) 通常比 (created_at, tenant_id, status) 更符合定位和排序需求,因为前两个等值条件先缩小范围,第三列还能支持范围和顺序扫描。

六、常见例外和优化器选择

最左前缀是理解 B+ 树联合索引的核心规则,但实际优化器可能有额外策略,如 skip scan、索引合并、统计信息驱动的成本选择。某些数据库在最左列基数很低时,可能尝试枚举最左列不同值来利用后续列,但这不是通用默认答案。

因此面试回答最好分两层:原理上,复合 key 字典序决定最左前缀;工程上,优化器可能做额外成本优化,但不能把例外当基本规则。

七、常见误区与追问

  • 误区:联合索引 (a,b,c) 等于同时拥有 abc 三个单列索引。 它是一棵按复合 key 排序的 B+ 树,不是三棵独立树。
  • 追问:where a=1 and c=3 能不能用索引? 可以用 a=1 定位范围,c=3 多数情况下作为过滤条件。
  • 误区:范围条件之后的列完全没价值。 它可能不能缩小定位范围,但可用于索引下推或覆盖索引。
  • 追问:为什么 b=2 不连续? 因为排序先按 a,不同 a 下的 b=2 分散在多个位置。
  • 误区:列选择性最高就一定放最前。 列顺序要结合等值、范围、排序、分组和真实查询频率。

八、加强记忆

最左前缀的根源是复合 key 的字典序。(a,b,c) 先按 a 排,a 相同才按 b,b 相同才按 c;所以从左连续约束越长,B+ 树越能定位到更窄的连续范围。跳过左列会散,范围条件会让后续列难以继续形成单段范围。记住「定位范围」和「扫描中过滤」是两件事,很多索引题就不会混。