现代数据库系统数据结构 B+Tree
B+ 树(B+ Tree) 是 B-树(B-Tree)的增强版,也是现代数据库系统(如 MySQL InnoDB、Oracle、PostgreSQL)和文件系统中最常用的索引结构。它在 B-树的基础上做了关键优化,特别适合范围查询和磁盘 I/O 高效访问。
下面从结构、性质、操作、优势、应用五个维度详细解析 B+ 树。
一、B+ 树的核心设计思想
目标:在外存(如磁盘) 上高效支持 等值查询、范围查询、顺序遍历,同时最小化磁盘 I/O 次数。
磁盘读写以页(Page) 为单位(通常 4KB~16KB),B+ 树将一个节点设计为恰好填满一页,这样一次磁盘 I/O 就能加载一个完整节点。
二、B+ 树的结构特点
✅ 1. 多路平衡搜索树
- 每个内部节点有 m 个子节点(m 称为阶数,通常 100~1000+)
- 所有叶子节点位于同一层,保证查询路径长度一致
✅ 2. 内部节点只存索引(关键字 + 指针),不存数据
- 关键字用于指引搜索方向
- 例如:节点
[10, 20, 30]表示:- <10 → 左子树
- 10~20 → 中间子树
- 20~30 → 右子树
-
30 → 最右子树
✅ 3. 所有数据记录只存储在叶子节点
- 叶子节点包含:
- 完整的关键字(Key)
- 对应的数据(Data)(或指向数据的指针,如主键 ID)
- 内部节点不存数据,只做路由
✅ 4. 叶子节点用双向链表连接
- 每个叶子节点有指针指向前一个和后一个叶子节点
- → 支持高效的范围查询和顺序扫描
三、B+ 树的性质(以阶数 m 为例)
假设 B+ 树的阶数为 m(即一个节点最多有 m 个子指针):
| 节点类型 | 关键字数量 | 子指针数量 | 最小填充 |
|---|---|---|---|
| 根节点 | 1 ~ m-1 | 2 ~ m | 可少于一半(但至少 2 个子节点,除非是叶子) |
| 内部节点 | ⌈m/2⌉-1 ~ m-1 | ⌈m/2⌉ ~ m | 至少半满 |
| 叶子节点 | ⌈m/2⌉ ~ m | 无子指针(但有链表指针) | 至少半满 |
📌 举例:若 m=4(4阶 B+ 树)
- 内部节点关键字数:1~3
- 叶子节点关键字数:2~4
- 根节点至少 2 个子节点(除非整棵树只有根)
四、B+ 树 vs B-树:关键区别
| 特性 | B-树 | B+ 树 |
|---|---|---|
| 数据存储位置 | 内部节点和叶子节点都存数据 | 仅叶子节点存数据 |
| 关键字冗余 | 每个关键字只出现一次 | 内部节点关键字会重复出现在叶子中(用于路由) |
| 范围查询 | 需中序遍历整棵树,效率低 | 叶子链表直接顺序遍历,极快 |
| I/O 效率 | 内部节点存数据 → 单页存储的关键字更少 | 内部节点只存索引 → 单页可存更多关键字,树更矮 |
| 查询稳定性 | 所有查询路径长度相同 | 所有数据查询必须走到叶子,路径长度完全一致 |
💡 B+ 树的优势:
- 更高的扇出(fan-out)→ 树高更低 → 更少 I/O
- 范围查询性能极佳(数据库常用
WHERE id BETWEEN 100 AND 200)- 顺序扫描高效(如
ORDER BY)
五、B+ 树操作示例(以 m=3 为例)
1. 查找(Search)
- 从根开始,逐层向下比较关键字
- 必须走到叶子节点才能获取数据
- 时间复杂度:O(logₘ n)
2. 插入(Insert)
- 先查找应插入的叶子节点
- 插入后若叶子节点关键字数 > m → 分裂(Split)
- 将中间关键字上移到父节点
- 原节点分裂为两个半满节点
- 可能引发连锁分裂直至根节点(根分裂则树高+1)
3. 删除(Delete)
- 删除叶子节点中的关键字
- 若节点关键字数 < ⌈m/2⌉ → 合并(Merge)或借用(Redistribute)
- 从兄弟节点借关键字,或与兄弟合并
- 可能引发连锁合并,但不会降低树高(除非根变空)
🔁 所有操作都保持平衡性和叶子链表连续性。
六、B+ 树在数据库中的应用(以 MySQL InnoDB 为例)
✅ 聚簇索引(Clustered Index)
- 主键索引就是 B+ 树
- 叶子节点存储整行数据
- 例如:
PRIMARY KEY (id)→ B+ 树叶子存完整记录
✅ 二级索引(Secondary Index)
- 非主键索引也是 B+ 树
- 叶子节点存储主键值(不是数据!)
- 查询时需回表:先查二级索引得主键,再查聚簇索引得数据
🌰 示例:
CREATE TABLE users (id INT PRIMARY KEY,name VARCHAR(50),age INT,INDEX idx_name (name)
);
PRIMARY KEY→ B+ 树,叶子存(id, name, age)idx_name→ B+ 树,叶子存(name, id)
七、为什么数据库偏爱 B+ 树?
| 需求 | B+ 树如何满足 |
|---|---|
| 减少磁盘 I/O | 高扇出 → 树高通常 2~4 层,10 亿数据只需 3~4 次 I/O |
| 高效范围查询 | 叶子链表支持 BETWEEN、>、ORDER BY |
| 顺序访问快 | 链表遍历无需回溯 |
| 插入/删除稳定 | 分裂/合并局部操作,不影响全局 |
| 缓存友好 | 节点大小 = 页大小,与 OS 缓存对齐 |
八、B+ 树的局限性
- 不适合频繁更新主键(会导致 B+ 树结构调整)
- 非主键查询需回表(可通过覆盖索引优化)
- 内存中不如红黑树灵活(B+ 树为外存设计)
总结
B+ 树 = B-树 + 数据下沉 + 叶子链表
它是为磁盘存储系统量身定制的索引结构,通过:
- 内部节点只存索引 → 提高扇出,降低树高
- 数据全在叶子 → 查询路径统一
- 叶子链表连接 → 范围查询飞快
正是这些设计,让现代数据库能在十亿级数据上实现毫秒级查询。
