B+Tree?

132 阅读9分钟

B+Tree到底是什么?有很多初学的小伙伴可能被各种资料,AI的一大堆专有名词搞得头昏脑胀,也没理解其核心原理,本文就谈一谈到底什么是B+Tree。 仅个人理解,如有不同理解或文中有错误欢迎讨论指出

从定义上来说,B+Tree是一种多路自平衡的树数据结构,其核心设计目标是为了减少磁盘的IO,优化查询效率,广泛的应用于数据库索引、文件系统等场景。其本质是由B-Tree的改进,B+Tree是B-Tree的“叶子强化、内部节点轻量化”的特殊多路平衡查找树。

一、核心定义:什么是B+Tree?

B+tree 是满足以下规则的多路平衡查找树(假设树的阶数为 mm 表示每个节点最多有 m-1 个关键字、m 个子节点指针)

1.节点分类

树由根节点非叶子节点也叫内部节点和叶节点组成,且所有叶节点处于同一层

为什么称其为树?顾名思义,其图像化的特征就像一颗大树一样。根节点是树干,非叶子节点是各个树枝的分枝点,叶节点是树叶,如下图

B+tree.png

2.关键字分布

  • 非叶子节点:仅存储关键字+子节点指针。此关键字为冗余索引关键字,不存储数据,仅用于索引导航

非叶子节点类似于我们生活中的标签,仅承担索引的职责。例如在冰箱中,不同的抽屉上用便利贴做了标签如生肉,蔬菜,冰淇淋,它并不具备实际的数据存储功能,只是为了在查询中更加方便快捷的找到想要的数据。

  • 叶节点:存储全部关键字+对应数据(或数据地址),叶节点一定处于树的最底层,且所有叶节点通过双向链表连接(加快顺序访问效率)。此关键字是原始且唯一关联数据的关键字,是非叶节点关键字的来源,是索引的核心

叶节点才是实际存储数据或存储数据地址的地方,数据地址是指数据在磁盘位置坐标

3.阶数规则(m阶)

  • 根节点:最少1个关键字,最多 m-1 个关键字(子节点数=关键字数+1,最少2个子节点,最多m个)
  • 非根非叶节点:最少 ⌈m/2⌉ - 1 个关键字,最多 m-1 个关键字(子节点数最少 ⌈m/2⌉ 个,最多 m 个)
  • 叶节点:最少 ⌈m/2⌉ - 1 个关键字,最多 m-1 个关键字,与非根非叶节点一致

子节点数即在此节点下有几个分支(即节点指针

4.查找规则

从根节点开始,按关键字有序性(升序/降序)遍历,通过数据关键字一层一层地比较并确定下一个子节点,最终找到数据所在的叶节点

二、结构原理

4阶B+Tree(m=4,每字节最多3个关键字、4个子节点指针)为例,结构如下图所示

PixPin_2025-12-01_20-57-15.png

最下面的箭头表示叶节点双向链表

  • 双向链表:叶节点是按照关键字有序排列的,且能通过前后指针连接,支持范围查询顺序访问,而无需回溯上层节点

三、核心特性:为什么 B+tree 适合磁盘存储?

B+tree 的设计完全围绕磁盘 I/O 优化展开,磁盘的核心特点是:随机访问速度极慢(毫秒级),顺序访问速度极快(机械硬盘的磁头无需重新定位) ,且磁盘 I/O 以为最小单位(通常 4KB/8KB)。B+tree 的特性完美适配这一特点:

1.平衡特性:所有叶节点均在最底层

  • B+Tree是严格平衡树,其叶节点高度一致,所以在查找任何数据时,I/O次数固定(也就是树的高度),这一特点完美避免了二叉树、红黑树等可能出现的斜树(例如在MySQL中的ID自增列)导致的I/O次数波动。
  • 树的高度极低:假设m=100(每个节点最多99个关键字),存储1亿条数据时,树的高度仅为log₁₀₀(1亿) = 4,即最多只需要4次磁盘I/O,就能找到目标数据。

2.非叶节点仅存储索引,存储密度极高

  • 非叶节点不存储数据,仅存储关键字 + 子节点指针,相同的磁盘块大小下,非叶节点能容纳更多关键字和指针,从而降低树的高度,降低 I/O 次数。
  • 对比 B 树B 树的非叶节点存储索引存储数据,导致相同节点大小下关键字数量更少,树更高,I/O 次数更多。

3. 叶节点链表化,支持高效范围查询

  • 更高效的范围查询:
    • B 树:需要遍历多个分支,回溯上层节点,效率
    • B+Tree:只需找到范围的起始叶节点(如 100),然后通过叶节点的双向链表顺序遍历,直到结束节点(如 200),无需回溯,效率接近顺序访问

4. 数据集中存储,缓存命中率高

  • 所有数据都在叶节点,且叶节点按顺序排列,磁盘预读机制(提前读取相邻块)能够有效发挥作用:当访问一个叶节点时,其相邻的叶节点可能已被预读入内存,后续查询无需额外 I/O。
  • 非叶节点体积小,更容易被缓存到内存中,进一步减少磁盘 I/O(如根节点和上层非叶节点几乎常驻内存)。

四、与其他树结构的对比:B 树、B+tree、B*tree、红黑树

PixPin_2025-12-01_22-35-47.png

  1. B 树 vs B+tree:B 树的非叶节点存储数据,适合单点查询(可能提前命中),但范围查询顺序访问效率差;B+tree 牺牲了单点查询的微小效率,换取了范围查询和高存储密度,更适合磁盘存储。
  2. B+tree vs B * tree:B * treeB+tree 的优化版,非叶节点之间增加了兄弟指针,并预留出 10% 节点空间用于减少分裂频率,适合高并发大数据量场景(如分布式数据库)。
  3. B+tree vs 红黑树:红黑树或者说二叉树,其高度随数据量增长为 O (log₂n)(如 1 亿数据需 27 层),I/O次数过多,仅适合内存数据结构;B+tree 是多路树,高度 O (logm n)(m 通常为 100+),I/O 次数极少,适合磁盘存储。

五、B+tree 的插入、删除与平衡维护

B+tree 在插入 / 删除数据时,会自主维持树的平衡性和节点关键字数量,核心操作包括节点分裂节点合并邻节点借调

我们以 4 阶 B+tree(m=4)为例,节点关键字数量范围:

  • 非根节点:最少 ⌈4/2⌉ - 1 = 1 个,最多 4-1=3 个;
  • 根节点:最少1个,最多3个;
  • 叶节点:最少1个,最多3个。

1. 插入操作(以插入关键字 33 为例)

步骤:
  1. 查找插入位置:从根节点出发,找到关键字 33 应插入的叶节点(假设为 [35,40,50],插入后变为 [33,35,40,50],此时超出最大数量 3);

  2. 节点分裂

    • 将分裂节点的关键字按中间位置拆分(4 个关键字拆分为前 2 个和后 2 个:[33,35] 和 [40,50]);
    • 中间关键字(35)向上提升到父节点(假设父节点为 [30,40,50],插入 35 后变为 [30,35,40,50],若父节点也超出最大数量,则继续分裂,直到根节点);
  3. 更新叶节点链表:分裂后的两个叶节点会自动更新前后指针,维持链表有序性

2. 删除操作(以删除关键字 35 为例)

步骤:
  1. 查找删除位置:找到关键字 35 所在的叶节点(假设为 [33,35,40],删除后变为 [33,40],仍满足最少数量 1,无需合并);

  2. 处理节点关键字不足:若删除后节点关键字数量 < 最少值时(如删除 [33] 后节点为空):

    • 尝试邻节点借调:向相邻节点借一个关键字(需同步更新父节点的索引关键字);
    • 借调失败(邻节点也仅存最少关键字):节点合并,将当前节点与邻节点合并,同时删除父节点中对应的索引关键字(若父节点关键字数量不足,继续向上合并,直到根节点)。

3. 平衡维护的核心原则

  • 插入时向上分裂,删除时向下合并 / 借调,确保所有叶节点高度一致

  • 非叶节点的关键字始终是叶节点关键字的冗余副本(索引),确保查找路径的正确性。

可视化工具推荐

B+ Tree Visualization - USFCA 可交互式插入/删除,大家可以去玩一下,可以非常直观的观察到分裂合并的过程。

PixPin_2025-12-03_19-46-53.png

六、总结

B+tree 的设计本质是用空间换时间,通过关键字冗余叶节点链表化,最大化降低磁盘 I/O 次数,适配磁盘存储的特性。其核心优势特点为:

  • 低 I/O 成本:树高度极低,单点查询、范围查询的 I/O 次数固定且极少;
  • 高效顺序访问:叶节点链表支持全表扫描、排序、分组等高频数据库操作;
  • 稳定性强:严格平衡特性,查询性能稳定,无极端情况。

适用场景:

  • 数据库索引(MySQL InnoDB/MyISAM、PostgreSQL);
  • 文件系统(NTFS、ext4 的目录结构);
  • 分布式存储(如 HBase 的 RegionIndex);
  • 任何需要大量数据 + 磁盘存储 + 高频查询 / 范围查询的场景。

不适用场景:

  • 内存中的小规模数据(红黑树、哈希表更高效);
  • 仅需单点查询、无范围查询需求(B 树或哈希表可能更优)。

通过理解 B+tree 的结构和设计思想,能更深入地优化数据库索引(如主键选择、联合索引设计)、文件系统性能,甚至在自定义存储系统中灵活应用其核心优化思路。

Bye~

7143456_58_4.jpg