B+Tree到底是什么?有很多初学的小伙伴可能被各种资料,AI的一大堆专有名词搞得头昏脑胀,也没理解其核心原理,本文就谈一谈到底什么是B+Tree。
仅个人理解,如有不同理解或文中有错误欢迎讨论指出
从定义上来说,B+Tree是一种多路自平衡的树数据结构,其核心设计目标是为了减少磁盘的IO,优化查询效率,广泛的应用于数据库索引、文件系统等场景。其本质是由B-Tree的改进,B+Tree是B-Tree的“叶子强化、内部节点轻量化”的特殊多路平衡查找树。
一、核心定义:什么是B+Tree?
B+tree 是满足以下规则的多路平衡查找树(假设树的阶数为 m,m 表示每个节点最多有 m-1 个关键字、m 个子节点指针)
1.节点分类
树由根节点、非叶子节点也叫内部节点和叶节点组成,且所有叶节点处于同一层。
为什么称其为树?顾名思义,其图像化的特征就像一颗大树一样。根节点是树干,非叶子节点是各个树枝的分枝点,叶节点是树叶,如下图
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个子节点指针)为例,结构如下图所示
最下面的箭头表示叶节点双向链表
- 双向链表:叶节点是按照关键字有序排列的,且能通过前后指针连接,支持范围查询和顺序访问,而无需回溯上层节点
三、核心特性:为什么 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、红黑树
- B 树 vs B+tree:B 树的非叶节点存储数据,适合单点查询(可能提前命中),但范围查询和顺序访问效率差;B+tree 牺牲了单点查询的微小效率,换取了范围查询和高存储密度,更适合磁盘存储。
- B+tree vs B * tree:B * tree是 B+tree 的优化版,非叶节点之间增加了兄弟指针,并预留出 10% 节点空间用于减少分裂频率,适合高并发、大数据量场景(如分布式数据库)。
- 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 为例)
步骤:
-
查找插入位置:从根节点出发,找到关键字 33 应插入的叶节点(假设为
[35,40,50],插入后变为[33,35,40,50],此时超出最大数量 3); -
节点分裂:
- 将分裂节点的关键字按中间位置拆分(
4个关键字拆分为前2个和后2个:[33,35]和[40,50]); - 中间关键字(35)向上提升到父节点(假设父节点为
[30,40,50],插入 35 后变为[30,35,40,50],若父节点也超出最大数量,则继续分裂,直到根节点);
- 将分裂节点的关键字按中间位置拆分(
-
更新叶节点链表:分裂后的两个叶节点会自动更新前后指针,维持链表有序性。
2. 删除操作(以删除关键字 35 为例)
步骤:
-
查找删除位置:找到关键字 35 所在的叶节点(假设为
[33,35,40],删除后变为[33,40],仍满足最少数量1,无需合并); -
处理节点关键字不足:若删除后节点关键字数量 < 最少值时(如删除
[33]后节点为空):- 尝试邻节点借调:向相邻节点借一个关键字(需同步更新父节点的索引关键字);
- 借调失败(邻节点也仅存最少关键字):节点合并,将当前节点与邻节点合并,同时删除父节点中对应的索引关键字(若父节点关键字数量不足,继续向上合并,直到根节点)。
3. 平衡维护的核心原则
-
插入时向上分裂,删除时向下合并 / 借调,确保所有叶节点高度一致;
-
非叶节点的关键字始终是叶节点关键字的冗余副本(索引),确保查找路径的正确性。
可视化工具推荐
B+ Tree Visualization - USFCA 可交互式插入/删除,大家可以去玩一下,可以非常直观的观察到分裂合并的过程。
六、总结
B+tree 的设计本质是用空间换时间,通过关键字冗余和叶节点链表化,最大化降低磁盘 I/O 次数,适配磁盘存储的特性。其核心优势特点为:
- 低 I/O 成本:树高度极低,单点查询、范围查询的 I/O 次数固定且极少;
- 高效顺序访问:叶节点链表支持全表扫描、排序、分组等高频数据库操作;
- 稳定性强:严格平衡特性,查询性能稳定,无极端情况。
适用场景:
- 数据库索引(MySQL InnoDB/MyISAM、PostgreSQL);
- 文件系统(NTFS、ext4 的目录结构);
- 分布式存储(如 HBase 的 RegionIndex);
- 任何需要
大量数据 + 磁盘存储 + 高频查询 / 范围查询的场景。
不适用场景:
- 内存中的小规模数据(红黑树、哈希表更高效);
- 仅需单点查询、无范围查询需求(B 树或哈希表可能更优)。
通过理解 B+tree 的结构和设计思想,能更深入地优化数据库索引(如主键选择、联合索引设计)、文件系统性能,甚至在自定义存储系统中灵活应用其核心优化思路。
Bye~