(b2)B-树:结构

官方信息技术老师·11 页·深入(追求细节与边界)·0 次浏览·3 天前
B-树结构阶与节点分裂机制磁盘存储

(b2)B-树:结构

看清每个节点的装填规则、分裂时机与高度控制

按 空格/→ 演示下一步

1 / 11 页

全部页面点击任意一页,跳回舞台从这页播放

B-树结构阶与节点分裂机制磁盘存储

(b2)B-树:结构

看清每个节点的装填规则、分裂时机与高度控制

1第 1 页 · (b2)B-树:结构

观察体验

前面我们从整体认识了 B-树的结构。现在换个角度——先不看定义,直接盯着一棵实例:每个节点装多个键,所有叶子齐刷刷落在同一层。

多键多子
单节点可存多个键,按序排列,键之间划分出多个子节点区间
等高叶子
所有叶子节点位于同一层,从根到叶的路径长度完全相等
阶数约束
m 阶树:根外内部节点子数 ∈ [⌈m/2⌉, m],根子数 ≥ 2
字典的字母分区索引对应 →B-树的多键节点

字典按字母分成多区,区间边界就是键,每区背后是一组词条(子树)

m/2子节点数m(根除外)\lceil m/2 \rceil \leq \text{子节点数} \leq m \quad (\text{根除外})
2第 2 页 · 观察体验

多路平衡

上页看到,每个节点能装多个 key、分多个分支,整棵树高度却被压得极低。这背后靠的就是 B-树最核心的结构性质——多路平衡。

多路:阶数 m
节点最多 m 个子节点、m−1 个 key,扇出由 m 控制
平衡:叶子同层
所有叶子深度相等,根到任一叶子的路径长度恒定
多路的目的
m 越大扇出越大,树高 h ≈ ⌈log_m((n+1)/2)⌉ 越矮
平衡的目的
统一路径长度,让查找/插入/删除稳定在 O(log_m n)
公司多分支汇报链对应 →B-树的多路平衡

一个经理直接管 m 个下属,下属再各管 m 个——扇出大、层级少,且每条汇报路径同级

h1+logm/2 ⁣(n+12)h \le 1 + \log_{\lceil m/2 \rceil}\!\left(\tfrac{n+1}{2}\right)
3第 3 页 · 多路平衡

还是I/O

上一页我们说 B-树『多路』又『平衡』——可你可能想问:为什么非得这么设计?答案很朴素:一切为了 I/O。

磁盘 I/O 是瓶颈
磁盘读写比内存慢 5-6 个数量级,树操作时间主要耗在磁盘上
一次 I/O 读一页
B-树以磁盘页/块为单位读写,不是逐个 key 读
矮 = I/O 少
高度为 h 的 B-树,单次查找最多 h 次磁盘 I/O
让节点装更满
增加每页 key 数就能压低树高,进而减少 I/O 次数
去图书馆搬书对应 →B-树读一页

找一本书时,你搬的是整箱书(页),而不是一页一页翻(key)

hlogmN(m=,N=key 总数)h \leq \lceil \log_m N \rceil \quad (m = \text{阶}, N = \text{key 总数})
4第 4 页 · 还是I/O

深度统一

上一页从磁盘I/O的角度看B-树,发现一个隐患:不同查找走过的层数如果不一样,磁盘读取次数就不可预测。B-树用一条铁律解决这个问题——所有叶子节点必须位于同一深度。

定义:所有叶子同层
不论数据多少、插入顺序如何,叶子深度严格相等,没有"差一层"的灰色地带
只在根分裂时增层
普通节点满就向上分裂产生兄弟;只有根节点一分为二时,树的高度才加1
比AVL更严格
AVL允许左右子树深度差最多1,B-树要求所有叶子绝对等深
I/O次数完全可预测
任何一次查找的磁盘读取次数是确定的常数,最坏性能可控
酒店电梯直达所有楼层对应 →B-树根到所有叶子等距

任意房间从大堂出发要坐的楼层数都一样,对应任意查找的磁盘读取次数都一致

5第 5 页 · 深度统一

阶次含义

上一页我们让节点尽量多装关键字以摊薄 I/O,可「最多装多少」必须有个数——这就是「阶 m」。麻烦的是:业界对 m 的定义不统一,相同数字背后其实差着将近一倍。

Knuth 定义
m 阶指子节点上限为 m,因此每个内部节点最多 m 个分支、关键字 ≤ m−1
Bayer 定义
m 阶指关键字上限为 2m,因此子节点 ≤ 2m+1,Bayer 1972 原文所用
同名不同物
说「4 阶 B-树」,Knuth 派最多 3 键,Bayer 派最多 8 键,整整差近一倍
共同不变量
不论哪种定义,节点关键字数都有上下限约束(根除外),这是 B-树的核心
衣服尺码 L对应 →B-树的「阶 m」

同样叫 L,各品牌大小各异;同样叫 m 阶,不同教材差近一倍——必须先问「按谁的定义」

6第 6 页 · 阶次含义

: 紧凑表示

上页说清了一棵 B-树节点的『阶』由几个键决定。但要把整棵树存到磁盘,光有节点还不够——键和指针该怎么排?紧凑表示给出了一个极端答案:把一切摊平。

定义
把 B-树所有键按从小到大的顺序,全部塞进一个连续的有序序列,原本『节点+指针』的结构不再显式存在
子树边界
哪个键属于哪个子树,不再靠指针指,而是靠『区间位置』隐式表达;要还原结构需另存一份范围表
极致紧凑
没有指针、没有空块浪费,所有空间都留给键本身;扫描时也是顺序读,磁盘最友好
代价
一动就要重建整棵树,无法支持原地插入/删除;只适合『建好就不再改』的场景
典型应用
只读索引、数据仓库快照、静态字典文件等一次性写好、长期查询的数据
期末成绩按学号排成一张大表对应 →B-树紧凑表示

不再分『每班一页』,所有人排成一列;想找某班同学,要靠额外记下的学号边界

7第 7 页 · : 紧凑表示

BTNode

上一页把多个键塞进一个连续空间——那就是一个 B-树节点。现在打开它,看里面到底装了什么。

节点定义
B-树最小独立单位,存键与子节点指针
内部组成
键数组、子指针数组、键数 n、叶子标志
键数边界
由阶 m 卡定上下限,保证树不失衡
叶子标记
leaf=true 时无孩子;否则恰好 n+1 个
操作粒度
插入分裂、删除合并都按节点执行
文件柜的一层抽屉对应 →BTNode

容量固定,满了换新抽屉或拆分;抽屉间按编号范围相连

m/21nm1\lceil m/2 \rceil - 1 \leq n \leq m - 1
8第 8 页 · BTNode

BTree

前面几页我们分别看了节点能装多键、树高统一、阶次含义等特征,但这些还像散落的零件。现在用一句话把它们拢成 B-树的正式定义。

形式定义
满足最小度数 t 约束:非根节点键数 ∈ [t-1, 2t-1],根至少 1 个键
根的豁免
根是唯一可以「少装」的节点——树非空时它必须存在,所以允许键数下界放宽
完美平衡
所有叶节点同深度,最长路径严格等于最短路径
高扇出低树高
高度 h ≤ log_t((N+1)/2),t=128 时百万键约 3 层
典型应用
数据库索引(InnoDB 的 B+树)、文件系统(NTFS/ext4)、键值存储引擎
磁盘页(4KB~16KB)对应 →B-树节点键数上下界

节点必须正好装入一页——太少浪费 I/O 带宽,太多装不下就必须分裂

9第 9 页 · BTree

本节要点

  • 多路摊薄I/O,深度统一是B-树的命脉
  • 节点子树数在⌈m/2⌉到m之间,根是唯一例外
  • 紧凑表示把多指针结构压成线性数组,遍历更友好
  • BTree管分裂合并路由,BTNode管单节点存取
延伸主题:B-树:插入与分裂B-树:删除与合并B+树:与B-树的差异
10第 10 页 · 本节要点

课后思考

三个开放问题,先独立思考再看参考答案,每题都值得在纸上画一画。

1为什么 B-树要求每个内部节点至少 ⌈m/2⌉ 个孩子,而不是只要求 ≥1?

参考答案下界若为 1,单孩子节点会拉长路径,树退化为细高形,深度趋近元素数,I/O 暴涨。⌈m/2⌉ 把树钉死在「胖矮」区间,保证深度为 O(log_m N)。

2磁盘页从 4KB 涨到 64KB,阶次 m 该怎么调?树高和单次 I/O 数据量会怎么变?

参考答案应让 m 尽量大,使节点刚好填满一页。m↑→树高↓→查询 I/O 次数↓,但单页 I/O 数据量↑,节点内维护(分裂/合并)代价也↑。这是阶次选择的核心权衡。

3如果允许某节点关键字数暂时少于 ⌈m/2⌉-1(暂时下溢),但仍强制所有叶子同深度,B-树的查找正确性会被破坏吗?

参考答案查找正确性只依赖「路径长度统一」,与节点是否下溢无关,所以查找仍正确。但删除无法收敛——总有节点欠债,结构长期失衡。破坏的是「写后不变形」的不变量。

11第 11 页 · 课后思考
(b2)B-树:结构 · 知识图解