跳到主要内容
约 5 分钟4 张卡

B+ 树

B+ 树用多路导航和有序叶子支持索引查找。本文讲解树结构、范围扫描和页面调整。实现示例采用 MySQL 8.0 InnoDB。

1. 面试核心​

先记住这几条,再读正文
  • 页结构。 B+ 树内部页保存导航键和子页指针,叶子页保存索引记录。卡 · B+ 树的结构
  • 树高与 I/O。 分支数和叶子容量决定树高,树高只代表根到叶子的导航层数。页面命中 Buffer Pool 时直接从内存读取,因此三层树不等于三次磁盘 I/O。卡 · 分支数与树高
  • 范围查询。 范围查询定位首条记录后,沿有序叶子继续扫描。
  • 插入分布。 随机键分散插入位置,递增键把新增记录集中到树的右端。卡 · 插入、删除与页分裂
  • 页合并。 InnoDB 默认页大小为 16KB,页合并默认阈值为 50%。删除后合并页面可腾出可复用空间,但不会直接缩小表文件。卡 · 页面合并

2. B+ 树的结构​

B+ 树是一棵高度平衡的多路搜索树。所有叶子节点位于同一深度。

  • 内部节点保存分隔键和子页指针。
  • 叶子节点保存按键值排列的索引记录。
  • 相邻叶子页通过链接支持顺序扫描。

B+ 树

“阶数”需要结合教材定义使用。有的教材指最大子节点数,有的指最大键数量。估算数据库索引时,直接使用每页容量更清楚。

3. 分支数与树高​

InnoDB 默认以 16KB 的页组织索引。内部页只存导航信息,因此能容纳较多分支。键越宽,同一页面能容纳的导航项越少。

假设内部页平均有 500 个分支。三层树的叶子页数量约为 500 × 500。再乘每个叶子页的平均记录数,才得到行数估算。记录头和填充率也影响每页容量。

查询命中 Buffer Pool 时,页面读取在内存中完成。因此,三层树可以少于三次磁盘读取。范围扫描和溢出页则会增加访问页数。

4. 等值查询​

等值查询从根页开始。每一层根据分隔键选择子页,直到目标叶子。在叶子页内继续定位目标记录。

树高随记录数按对数增长。用平均分支数 f 表示时,导航成本记为 O(log_f N)。

InnoDB 的叶子记录分工见聚簇索引。

5. 范围查询​

范围查询先定位起点,再沿叶子顺序扫描。

SELECT * FROM users
WHERE id >= 10000 AND id < 10100;

定位 10000 后,扫描持续到第一条不小于 10100 的记录。中间各行共享这次树导航。索引键顺序与排序要求一致时,还能省去额外排序。

哈希表按哈希值定位,适合等值查找。B+ 树额外保留键顺序,能直接支持范围扫描。经典 B 树还在内部节点保存数据记录。B+ 树将记录集中在叶子层,内部页可容纳更多导航项。

6. 插入、删除与页分裂​

插入先定位目标叶子页。页面有足够空间时直接插入。页内重整后仍放不下新记录时,需要拆分页面。父页随之增加导航项。分裂传播到根页时,树高增加。

删除释放的空间可供后续写入复用。页面合并的具体条件见下文。修改索引键需要移除旧索引项,再插入新项。

随机键把插入分散到多个叶子页。相同写入量下,需要访问的活跃页面更多。递增键集中追加到右端,具有较好的写入局部性。高并发写入也会争用末端页面。

7. 页面合并​

MySQL 8.0 的 MERGE_THRESHOLD 默认是 50。删除或缩短记录后,页使用率低于阈值时会尝试合并。相邻页必须有空间容纳合并后的记录。

页内空闲空间由 InnoDB 后续复用。表文件是否缩小还取决于表空间和重建操作。因此,删除大量记录后,文件大小可以保持不变。

innodb_fill_factor 控制排序建索引时的填充比例。它与运行期间的合并阈值分属不同阶段。

8. 查询成本​

B+ 树支持有序访问。扫描范围、回表和统计估算的评估见索引设计。

9. 常见问题​

9.1 叶子顺序​

有序叶子把相邻键放在同一条扫描路径上。范围查询只需定位起点,随后顺序读取。排序与键顺序一致时,也能直接输出有序结果。

9.2 树高与磁盘读取​

10 · 面试题卡片​

本页题目与参考回答

10.1 MySQL 为什么常用 B+ 树索引,而不直接使用二叉树或哈希表​

考察重点

  • 数据页、分支数量与树高。
  • 等值、范围和排序访问。
  • 叶子记录、回表和写入维护成本。

相关内容:B+ 树的结构至查询成本。

参考回答

B+ 树同时支持低树高导航和有序范围扫描。

多路内部页比二叉结构容纳更多分支。同样的记录量因此需要更少导航层。叶子有序,范围查询可在定位后继续读取。

哈希表适合等值查找,范围和排序需要额外结构。B+ 树的代价是索引空间和页面维护。实际查询还需计入扫描量、缓存命中和回表成本。

10.2 三层 B+ 树查询是否一定发生三次磁盘 I/O?​

考察重点

  • 树高与缓冲池命中
  • 回表、跨页与溢出页

相关内容:分支数与树高、等值查询。

参考回答

三层 B+ 树不固定对应三次磁盘 I/O。

导航页命中 Buffer Pool 时直接从内存读取。范围跨页、溢出页和回表还会增加页面访问。树高只描述根到叶子的导航层数。

10.3 随机主键和递增主键分别影响哪些索引成本?​

考察重点

  • 键宽与分支数
  • 页分裂和并发热点

相关内容:插入、删除与页分裂。

参考回答

随机键分散写入页面,递增键集中向右端追加。

前者需要缓存更多活跃页,后者有末端争用风险。主键宽度还会影响二级索引的记录大小。插入分布与键宽需要分别评估。

10.4 删除大量记录后,B+ 树页面和表文件会怎样变化?​

考察重点:页面利用率、合并条件和空间复用。

相关内容:页面合并。

参考回答

页使用率降低后,InnoDB 会尝试合并相邻页。

MySQL 8.0 默认 MERGE_THRESHOLD=50。合并需要相邻页有足够空间。释放的页可供表空间复用,文件缩小另需重建等操作。

11. 参考资料​