B* 树为什么要求节点更高填充率?它的分裂策略和 B 树有什么不同?
简化版
B* 树是 B 树家族的变体,通常要求节点至少约 2/3 满,而不是 B 树常见的至少半满。插入溢出时,B* 树会优先和兄弟节点重新分配;兄弟也满时,才把两个满节点和新 key 分裂成三个节点,从而提高空间利用率。
详细版
B 树节点满了通常直接分裂成两个节点,分裂后每个节点大约半满。B* 树为了提高页利用率,引入兄弟再分配和 2-to-3 split:
- 当前节点满;
- 先看相邻兄弟是否有空间;
- 若兄弟有空间,把 key 在当前节点、父分隔 key、兄弟之间重新分配;
- 若兄弟也满,把两个节点加上新 key 分成三个节点;
- 父节点插入新的分隔 key。
这样节点最低填充率可以提高到约 2/3,树更紧凑,空间利用率更好,但实现比普通 B 树分裂复杂。
完整版教学
一、B* 树想解决 B 树的什么问题
B 树分裂后两个节点通常各半满。这样简单,但空间利用率可能不高:大量页只有一半左右数据,索引文件更大,缓存效率也会下降。B* 树的目标是提高节点填充率,让每个页尽量更满。
在磁盘页作为节点的场景下,页利用率非常重要。一个页装得越满,同样数据需要的页越少,缓存和 I/O 压力越低。B* 树就是在分裂策略上更节省空间。
记忆钩子:B 树满了就一分为二,B* 树先找兄弟挤一挤,实在挤不下再二变三。
二、普通 B 树分裂为什么可能只有半满
普通 B 树节点满后,把中间 key 上提,左右两边分成两个节点。若一个节点最多放 5 个 key,插入后临时有 6 个 key,分裂成两个节点后可能各 2 到 3 个 key。填充率大约在 50% 附近。
[10 20 30 40 50] 插入 35
临时: [10 20 30 35 40 50]
分裂: [10 20] 30 [35 40 50]
这种策略实现清晰,但没有充分利用兄弟页可能还有空位的事实。
三、B* 树的再分配怎么做
当当前节点满时,B* 树会先检查相邻兄弟。如果兄弟没满,就把当前节点、父节点分隔 key、兄弟节点中的 key 合在一起重新分配,让两个节点都保持较高填充率,同时更新父分隔 key。
例如两个兄弟页容量都是 6:
左页: [10 20 30 40 50 60] 已满
右页: [80 90] 未满
插入 55 后,可把部分 key 移到右页,而不是立刻新建页
这样避免了一次页分裂,减少父节点更新,也提高页利用率。
四、两个满节点为什么分成三个
如果当前节点和兄弟都满,就把两个满节点、父分隔 key、新插入 key 合并后分成三个节点。两个满节点容量合计是 2m,加上新 key 和分隔信息后分成三个节点,每个节点大约 2/3 满。
2 个满页 + 1 个新 key -> 3 个较满页
填充率约从 1/2 提升到 2/3
这就是 B* 树比普通 B 树空间利用率更高的核心。代价是分裂不再只看一个节点,还要协调兄弟和父节点。
五、B* 树和 B+ 树是什么关系
B* 树和 B+ 树关注点不同。B+ 树强调所有数据在叶子、叶子链表支持范围查询;B* 树强调更高节点填充率和兄弟再分配。实际系统可能组合多种思想,但教材里它们是 B 树家族的不同变体。
| 结构 | 重点 | 常见特征 |
|---|---|---|
| B 树 | 多路平衡查找 | 内部节点可存数据 |
| B+ 树 | 范围查询和磁盘索引 | 数据在叶子,叶子链表 |
| B* 树 | 提高空间利用率 | 兄弟再分配,2-to-3 split |
面试时不要把 B* 树简单说成 B+ 树的另一个名字,它强调的是分裂和填充率策略。
六、为什么工程里不一定总讲 B* 树
B* 树提高空间利用率,但实现更复杂。现代数据库存储引擎还会结合填充因子、页分裂策略、后台整理、压缩、MVCC 等机制综合优化。很多面试只要求理解 B/B+ 树,B* 树属于加分扩展。
如果被问到 B* 树,重点答出「至少 2/3 满」「先兄弟再分配」「两个满节点分成三个」就足够清晰。不要把它扩展成完全不同的索引模型。
七、常见误区与追问
- 误区:B星树就是 B+ 树。 B* 树主要强调更高填充率和分裂策略,B+ 树强调叶子存数据和链表。
- 追问:为什么最低填充率能到约 2/3? 两个满节点加新 key 分成三个节点,平均每个约三分之二满。
- 误区:节点满了必须马上一分为二。 B* 树会先尝试和兄弟重新分配。
- 追问:B星树代价是什么? 实现更复杂,分裂要协调兄弟和父节点。
- 误区:填充率越高越没有缺点。 太满会增加后续插入分裂概率,工程上仍需平衡。
- 追问:B星树为什么要先看兄弟节点? 因为兄弟有空位时重新分配比新增页更省空间,也能减少父节点结构变化。
- 误区:B星树能避免所有页分裂。 它只能延后和减少分裂;兄弟也满时仍然需要 2-to-3 split。
八、加强记忆
B* 树可以记成「更抠空间的 B 树」。普通 B 树满了就分裂成两个半满节点;B* 树先找兄弟借空间,兄弟也满时再把两个节点分成三个,让页保持约 2/3 以上填充。它不是 B+ 树的同义词,而是 B 树家族里围绕填充率和分裂策略的一种改进。