摘要
B树与B+树作为经典的多路平衡查找树,是磁盘型数据库与文件系统的核心索引结构。本文从二叉排序树与平衡二叉树的局限性出发,系统阐述了B树的阶数定义、节点取值约束及四条核心性质;在此基础上,深入剖析了B+树作为B树变体的结构特征,重点论述了二者在关键字与子树数量关系、根节点约束、终端节点链接方式、数据分布策略及关键字冗余机制上的本质差异。进一步地,本文揭示了B+树通过分离索引与数据、压缩内部节点及链式连接叶子节点以降低磁盘I/O次数的底层优化机理。研究表明,B+树在磁盘存储场景下的范围查询性能与空间局部性显著优于B树,因而成为现代关系型数据库管理系统的首选索引结构。
关键词:B树;B+树;多路平衡查找树;磁盘I/O;数据库索引;空间局部性
1. 引言
在计算机科学中,树形结构是组织有序数据的基础工具。二叉排序树(Binary Search Tree, BST)通过严格的左右子树大小关系实现快速查找,但其性能高度依赖于树的形态------在极端情况下,数据按有序序列插入将导致树结构严重倾斜,查找时间复杂度退化至 O(N)O(N)O(N)。为克服此缺陷,平衡二叉树(如AVL树、红黑树)通过旋转操作维持树的平衡,将查找时间复杂度稳定控制在 O(log2N)O(\log_2 N)O(log2N) 1。然而,平衡二叉树仍为二叉分支结构,每个节点至多拥有两个子节点,在海量数据场景下树高依然偏高,导致磁盘I/O次数过多,难以满足外存系统的性能需求。
B树(B-Tree)由Bayer与McCreight于1972年提出,其核心思想是将二叉平衡查找树扩展为多路平衡查找树(Balanced Multi-way Search Tree),使单个节点可存储多个关键字,从而大幅降低树高 2。B+树(B±Tree)作为B树的变体,通过将数据记录全部下沉至叶子节点、内部节点仅保留索引关键字,并引入叶子节点间的链式指针,进一步优化了范围查询与磁盘访问效率 3。如今,B+树已成为MySQL InnoDB、PostgreSQL等主流关系型数据库管理系统的标准索引实现。
本文旨在系统梳理B树与B+树的形式化定义、核心性质及结构差异,并深入分析B+树适配磁盘存储的优化机理,为数据库索引设计与数据结构教学提供理论参考。
2. B树的形式化定义与核心性质
2.1 阶的定义与节点结构
定义1(B树的阶) :设 MMM 为正整数且 M≥3M \geq 3M≥3,若一棵B树中所有节点拥有的子节点个数的最大值为 MMM,则称该树为 MMM 阶B树。
需要特别指出的是,阶数 MMM 是针对整棵树的全局定义,而非单个节点的局部属性。在B树的图示规范中,通常仅绘制存储有效关键字的非空节点,查找失败节点(空节点)不予显示,但这些空节点在逻辑上真实存在,是判定阶数的关键依据。根据B树的结构规则,一个包含 nnn 个关键字的节点必然对应 n+1n+1n+1 棵子树(分支)。因此,若某终端节点存储了3个关键字,则其对应4个分支,其中可见部分为有效子节点,剩余为未绘制的查找失败节点;该节点决定了整棵树的最大子节点数为4,故为一棵4阶B树。
注:关于终端节点与叶子节点的术语,不同教材存在差异。部分文献将存储有效关键字的最下层节点称为终端节点,将查找失败节点称为叶子节点;亦有文献定义相反。本文采用"查找失败节点"指代空节点,以避免歧义。
2.2 核心性质
任意一棵合法的 MMM 阶B树必须严格满足以下四条性质:
性质1(子树数量上限) :每个节点至多拥有 MMM 棵子树。此性质与阶数定义直接对应,即所有节点的子树数均不超过 MMM。
性质2(关键字与子树的数量关系) :对于任意一个拥有 kkk 棵子树的非叶子节点,其内部恰好包含 k−1k-1k−1 个关键字(Key)。该性质是B树最核心的数量规律。从节点结构看,可形式化表示为:
P0,K1,P1,K2,P2,...,Kk−1,Pk−1P_0, K_1, P_1, K_2, P_2, \dots, K_{k-1}, P_{k-1}P0,K1,P1,K2,P2,...,Kk−1,Pk−1
其中 KiK_iKi 为关键字,PiP_iPi 为指向子树的指针。由此可得通用公式:
非叶子节点子树数量=关键字数量+1\text{非叶子节点子树数量} = \text{关键字数量} + 1非叶子节点子树数量=关键字数量+1
性质3(子树数量下限------根节点除外) :除根节点外,所有非叶子节点至少拥有 ⌈M/2⌉\lceil M/2 \rceil⌈M/2⌉ 棵子树,对应至少包含 ⌈M/2⌉−1\lceil M/2 \rceil - 1⌈M/2⌉−1 个关键字。该约束保证了B树的多路平衡特性,防止结构倾斜。
性质4(叶子节点同层):所有查找失败节点(叶子节点)均出现在同一层级,整棵树的所有分支高度完全一致。这是B树平衡特性的核心体现,也是其区别于普通多路树的关键标志。
2.3 节点取值范围汇总
综合上述性质,MMM 阶B树各节点的取值范围如下:
| 节点类型 | 关键字数量 | 子树数量 |
|---|---|---|
| 根节点(非终端) | 1,M−11, M-11,M−1 | 2,M2, M2,M |
| 根节点(仅自身) | 1,11, 11,1 | 0,00, 00,0 |
| 普通非叶子节点 | ⌈M/2⌉−1,M−1\\lceil M/2 \\rceil - 1, M-1⌈M/2⌉−1,M−1 | ⌈M/2⌉,M\\lceil M/2 \\rceil, M⌈M/2⌉,M |
2.4 B树结构示例
如图1所示为一棵4阶B树。该树根节点存储关键字 {10}\{10\}{10},包含2棵子树;第二层左子节点存储关键字 {5}\{5\}{5},包含2棵子树,右子节点存储关键字 {15,20}\{15, 20\}{15,20},包含3棵子树;第三层为终端节点,分别存储关键字 {1,3,4}\{1, 3, 4\}{1,3,4}、{6}\{6\}{6}、{12}\{12\}{12}、{16,18}\{16, 18\}{16,18} 以及 {25}\{25\}{25}。

该示例严格满足4阶B树的全部性质:
- 子树数量上限 :所有节点的子树数均不超过4。右子节点 {15,20}\{15, 20\}{15,20} 含2个关键字、3棵子树,满足 3≤43 \leq 43≤4。
- 关键字与子树关系 :非叶子节点子树数 = 关键字数 + 1。根节点关键字数为1,子树数为2;第二层节点 {15,20}\{15, 20\}{15,20} 关键字数为2,子树数为3,均严格满足性质2。
- 子树数量下限 :除根节点外,所有非叶子节点至少拥有 ⌈4/2⌉=2\lceil 4/2 \rceil = 2⌈4/2⌉=2 棵子树。第二层左节点 {5}\{5\}{5} 含2棵子树,右节点 {15,20}\{15, 20\}{15,20} 含3棵子树,均满足下限约束。根节点 {10}\{10\}{10} 作为非终端根节点,仅需满足至少2棵子树,此处恰为2棵,符合根节点的特殊宽松规则。
- 叶子节点同层:所有终端节点均处于第三层,高度一致。
此外,图中每个节点均包含指向数据记录的指针(绿色箭头所示),表明B树的所有节点均直接承载完整数据记录,数据分布呈现"逐层散布"特征。
3. B+树的形式化定义与结构特征
B+树是B树的优化变体,在保持多路平衡特性的基础上,对节点结构、数据分布及终端节点组织方式进行了关键改造。
3.1 节点关键字与子树数量关系
与B树不同,MMM 阶B+树的非叶子节点中,关键字个数与子树个数完全相等 。即对于拥有 kkk 棵子树的非叶子节点,其恰好包含 kkk 个关键字,不再遵循B树"子树数 = 关键字数 + 1"的规则。
基于这一规则,B+树非叶子节点(除根外)的关键字与子树数量取值区间统一为 ⌈M/2⌉,M\\lceil M/2 \\rceil, M⌈M/2⌉,M。根节点若非终端节点,则至少拥有2棵子树、对应2个关键字,彻底统一了根节点与普通节点的下限约束,规避了B树根节点特殊宽松的问题。
3.2 终端节点结构与链式连接
B+树的所有终端节点(叶子节点)均处于同一层级,保证了绝对平衡。与B树的关键差异在于:B+树将所有终端节点通过双向指针串联成有序链表,而B树的叶子节点相互独立、无任何横向连接。这一设计使得B+树在执行范围查询(Range Query)或顺序遍历时,仅需定位起始叶子节点,随后沿链表线性扫描即可,无需回溯至根节点重复查找。
3.3 关键字存储与数据分布策略
B+树采用了严格的分层存储策略:
- 非叶子节点:仅存储关键字索引(Index),不存储实际数据记录。这些关键字通常为其子树中最大(或最小)关键字的副本,仅用于路径检索与定位;
- 叶子节点:存储全部关键字及对应的完整数据记录(或数据记录的指针)。
因此,B+树中同一个关键字可能在树中多次出现(于各级非叶子节点及叶子节点中重复存储),而B树中所有关键字全局唯一、仅存储一次。这种看似冗余的设计,实则是为了最大化内部节点的扇出(Fan-out),降低树高,从而减少磁盘I/O次数。
3.4 关键字冗余与索引副本机制
B+树中关键字的重复出现并非偶然,而是其索引路由机制的必然要求。具体而言,非叶子节点中的每个关键字 KiK_iKi 均为其对应子树中最大(或最小)关键字的副本(Replica),仅作为路由决策的边界值,不承载实际数据。
形式化描述 :设非叶子节点包含关键字序列 {K1,K2,...,Kn}\{K_1, K_2, \dots, K_n\}{K1,K2,...,Kn} 及子树指针 {P0,P1,...,Pn}\{P_0, P_1, \dots, P_n\}{P0,P1,...,Pn},则对于任意 i∈1,ni \in 1, ni∈1,n,关键字 KiK_iKi 的值等于子树 Pi−1P_{i-1}Pi−1 中所有关键字的最大值(或等于子树 PiP_iPi 中所有关键字的最小值,取决于具体实现)。该副本机制确保了查找过程中路由决策的正确性:当查询值 v≤Kiv \leq K_iv≤Ki 时,沿 Pi−1P_{i-1}Pi−1 向下检索;当 v>Kiv > K_iv>Ki 时,继续向右比较或沿 PiP_iPi 检索。
与B树的本质差异:在B树中,关键字具有全局唯一性,每个关键字与其数据记录一一对应,删除或修改关键字将直接影响数据完整性。而在B+树中,非叶子节点的关键字仅作为"路标",即使某关键字在非叶子节点中被修改或删除(如分裂操作),只要叶子节点中的原始记录保持不变,数据完整性就不会受损。这种"索引与数据解耦"的设计,是B+树能够高效维护内部节点、频繁执行节点分裂与合并而不影响底层数据的根本原因。
3.5 B+树结构示例
如图2所示为一棵B+树。该树根节点存储关键字 {8,15}\{8, 15\}{8,15},包含2棵子树;第二层左子节点存储关键字 {2,5,8}\{2, 5, 8\}{2,5,8},包含3棵子树,右子节点存储关键字 {11,15}\{11, 15\}{11,15},包含2棵子树;第三层为叶子节点,分别存储关键字 {1,2}\{1, 2\}{1,2}、{3,5}\{3, 5\}{3,5}、{6,8}\{6, 8\}{6,8}、{8,11}\{8, 11\}{8,11} 以及 {13,15}\{13, 15\}{13,15},各叶子节点通过双向链表依次连接(绿色箭头所示)。

该示例集中体现了B+树区别于B树的核心特征:
- 子树数与关键字数相等 :根节点含2个关键字、2棵子树;第二层左节点含3个关键字、3棵子树,均满足B+树"子树数 = 关键字数"的规则。对比图1中B树根节点 {10}\{10\}{10}(1个关键字对应2棵子树),二者在数量关系上存在本质差异。
- 关键字冗余与副本机制 :关键字8同时出现于根节点、第二层左子节点及叶子节点 {6,8}\{6, 8\}{6,8} 与 {8,11}\{8, 11\}{8,11} 中;关键字15同时出现于根节点、第二层右子节点及叶子节点 {13,15}\{13, 15\}{13,15} 中。这正体现了3.4节所述的索引副本机制------非叶子节点中的8和15仅作为子树路由的路标副本,叶子节点中的8和15才承载真实数据。
- 叶子节点链式连接 :图2中所有叶子节点通过绿色箭头串联为有序双向链表,支持范围查询的顺序遍历。而图1中B树的终端节点 {1,3,4}\{1, 3, 4\}{1,3,4}、{6}\{6\}{6} 等相互独立,执行范围查询时需反复回溯根节点。
- 数据分布分离:与图1中所有节点均含数据指针不同,图2中仅最下层叶子节点承载真实数据(图中以"data"标注),上层节点仅作检索路由。这种"索引与数据分离"的策略是B+树提升磁盘I/O效率的关键。
4. B树与B+树的系统性对比
4.1 核心差异对比
表1从六个维度系统对比了B树与B+树的核心差异。
表1 B树与B+树核心差异对比
| 对比维度 | B树 | B+树 |
|---|---|---|
| 关键字与子树关系 | 子树数 = 关键字数 + 1 | 子树数 = 关键字数 |
| 根节点下限(非终端) | 至少1个关键字、2棵子树 | 至少2个关键字、2棵子树 |
| 终端节点连接 | 相互独立,无横向链接 | 通过双向链表有序串联 |
| 数据存储位置 | 所有节点均存储完整数据 | 仅叶子节点存储数据,非叶节点仅存索引 |
| 关键字唯一性 | 全局唯一,仅出现一次 | 可重复出现,非叶节点关键字为索引副本 |
| 关键字语义 | 关键字即数据标识,直接关联记录 | 非叶节点关键字为路由路标,叶子节点关键字为真实数据 |
4.2 图示对比分析
结合图1与图2的具体实例,可进一步直观展示上述差异。在节点组织层面,图1中B树根节点 {10}\{10\}{10} 含1个关键字、2棵子树,遵循"子树数 = 关键字数 + 1"规则;而图2中B+树根节点 {8,15}\{8, 15\}{8,15} 含2个关键字、2棵子树,遵循"子树数 = 关键字数"规则。在数据分布层面,图1中每个节点(包括内部节点与终端节点)均包含指向数据记录的指针,数据分散存储于各级节点;图2中仅最下层叶子节点包含数据,内部节点仅作索引路由,且根节点中的15与第二层右节点中的15均为叶子节点 {13,15}\{13, 15\}{13,15} 中15的索引副本,体现了B+树的关键字冗余机制。在遍历效率层面,图1执行范围查询需从根节点出发反复查找;图2中叶子节点的双向链表使得范围查询仅需一次线性扫描,时间复杂度从 O(K⋅logMN)O(K \cdot \log_M N)O(K⋅logMN) 降至 O(logMN+K)O(\log_M N + K)O(logMN+K)(其中 KKK 为结果集大小)。
上述差异直接决定了二者在磁盘存储场景下的性能分野。B树由于每个节点均承载完整数据记录,单个磁盘块(Block)可容纳的节点数量与关键字数量极为有限,导致树的有效分支减少、检索路径延长。B+树通过将数据全部下沉至叶子节点,使内部节点得以轻量化,单个磁盘块可容纳的索引项数量呈数量级增长。
5. B+树的磁盘优化机理
现代计算机系统中,磁盘(Disk)以块(Block,通常为4 KB或16 KB)为基本读写单位,一次磁盘I/O操作会读取整块数据。B+树的结构设计深度适配了这一物理特性,其优化机理主要体现在以下三方面:
5.1 降低磁盘I/O次数
在真实业务场景中,数据表的完整记录往往体量庞大,而索引关键字仅为简短标识。B树每个节点同时存储关键字与完整数据,导致单个磁盘块容纳的索引项极少,查找时需频繁读取多个磁盘块。B+树的内部节点仅存储轻量级关键字索引,不存储完整数据,使得单个磁盘块可容纳的索引项数量呈数量级增长。因此,大部分检索路径仅需遍历少量内部节点所在的磁盘块,最终只需一次I/O访问叶子节点即可获取真实数据,显著减少了磁盘I/O次数。
5.2 提升空间局部性与缓存命中率
由于B+树内部节点不包含数据信息,索引数据在磁盘块中存放得更加紧密,具有更好的空间局部性(Spatial Locality)。当访问某叶子节点上的数据时,其相邻数据在物理存储上亦大概率位于邻近区域,从而提高了预读(Prefetching)效率与缓存命中率(Cache Hit Rate)。相比之下,B树的数据分散于各级节点,相邻元素在内存中可能不相邻,缓存局部性较差。
5.3 高效的范围查询与遍历
B+树叶子节点的双向链表结构使得对整棵树的便利遍历仅需一次线性扫描即可完成。对于 BETWEEN、ORDER BY 等范围查询操作,引擎定位到起始叶子节点后,可直接沿链表顺序访问后续节点,时间复杂度为 O(logMN+K)O(\log_M N + K)O(logMN+K)。而B树需对每一层递归遍历,范围查询效率远低于B+树。
6. 结论
本文系统阐述了B树与B+树的形式化定义、核心性质及结构差异,并结合具体图示实例深入剖析了二者的节点组织、数据分布与遍历效率差异。B树通过多路平衡机制解决了二叉平衡树在海量数据场景下的树高问题;B+树则在此基础上,通过"内部节点纯索引、叶子节点存数据、叶子层链式连接、非叶节点关键字副本路由"的多重设计,进一步优化了磁盘I/O效率、空间局部性与范围查询性能。研究表明,B+树在磁盘存储环境下的综合性能显著优于B树,这正是MySQL InnoDB、PostgreSQL等现代关系型数据库普遍采用B+树作为底层索引结构的核心原因。
未来研究可进一步关注B+树在固态硬盘(SSD)及非易失性内存(NVM)等新型存储介质上的适应性优化,以及LSM-Tree等新型索引结构在写密集型场景下对B+树的补充与替代方案。
参考文献
1 CORMEN T H, LEISERSON C E, RIVEST R L, et al. Introduction to Algorithms (3rd ed.)M. Cambridge: MIT Press, 2009: 308-338.
2 BAYER R, MCCREIGHT E. Organization and Maintenance of Large Ordered IndicesJ. Acta Informatica, 1972, 1(3): 173-189.
3 GRAEFE G. Modern B-Tree TechniquesJ. Foundations and Trends in Databases, 2011, 3(4): 203-402.
4 SILBERSCHATZ A, KORTH H F, SUDARSHAN S. 数据库系统概念(原书第7版)M. 杨冬青, 李红燕, 唐世渭, 等译. 北京: 机械工业出版社, 2021: 287-320.
5 严蔚敏, 吴伟民. 数据结构(C语言版)M. 北京: 清华大学出版社, 2021: 233-250.
6 姜承尧. MySQL 技术内幕:InnoDB 存储引擎(第2版)M. 北京: 机械工业出版社, 2013: 186-225.