Skip to content

06. 平衡二叉树与 B+ 树数据库索引机制 ​

💡 交互图解

本章理论对应的动态微观仿真位于:👉 CS 10 图全屏走查 · FIG 09 平衡树旋转变色 与 FIG 10 B+ 树分裂与范围扫描

1. 从二叉查找树的退化说起 ​

普通的二叉查找树(BST)在输入有序或近似有序的数据时(如依次插入 1,2,3,4,5),会退化为一条单向倾斜的线性链表。查询时间复杂度从优雅的 O(log⁡n) 瞬间堕落为 O(n)。

为了解决这一问题,诞生了以旋转与重平衡为核心的两大经典二叉平衡树体系:

  1. AVL 树(Adelson-Velsky and Landis):以严格高度差保持完全平衡;
  2. 红黑树(Red-Black Tree):以颜色与黑高约束保持统计意义上的弱平衡。

2. AVL 树与四种旋转平衡动作 ​

每个节点维护一个平衡因子(Balance Factor, BF):

BF=Height(左子树)−Height(右子树)

AVL 树要求任何节点的 |BF|≤1。一旦插入导致出现 |BF|==2,通过四种旋转恢复:


3. 红黑树的五大公理与变色决策树 ​

红黑树放弃了 AVL 的严格高度平衡,引入节点着色规则:

  1. 每个节点要么是红色,要么是黑色;
  2. 根节点是黑色的;
  3. 每个叶子节点(NIL 哨兵空节点)是黑色的;
  4. 如果一个节点是红色的,则它的子节点必须全为黑色(不能出现连续红节点);
  5. 从任一节点到其所有后代叶节点的简单路径上,均包含相同数目的黑色节点(黑高平衡,Black Height)。

红黑树核心性质:从根到叶子的最长可能路径(红黑交替)不会超过最短路径(全黑)的 2 倍。树高至多为 2log2⁡(n+1)。

插入修复核心决策表 ​

新插入节点默认着为红色。若其父节点为黑色,直接插入完毕无需修复;若父节点为红色(违反性质 4):

情况 (Case)判定条件修复动作
Case 1叔叔节点也是红色纯变色:父节点与叔叔节点变黑,祖父节点变红;将当前节点指针移到祖父,继续向上回溯检查
Case 2叔叔是黑色,当前是右孩子(内侧)先局部单旋:将当前节点左旋,转变为 Case 3
Case 3叔叔是黑色,当前是左孩子(外侧)旋转 + 变色:父节点变黑,祖父变红,对祖父右旋!彻底恢复平衡,终止修复!

4. 为什么数据库(MySQL InnoDB)必须选择 B+ 树? ​

如果把红黑树或 AVL 树直接搬到磁盘上做数据库索引,一个拥有 2000 万行的表,二叉树的高度约为 log2⁡(20,000,000)≈25 层。 由于二叉树每个节点只能存一个键值,每次树下跳基本对应一次磁盘随机寻道 I/O(机械硬盘一次 10ms,固态硬盘数十微秒)。查询一条记录需要 25 次磁盘 I/O,数据库将彻底卡死在 I/O 队列中。

B+ 树打败 B 树的决定性优势 ​

  1. 极其巨大的扇出(High Fanout)与极矮的树高:
    • 非叶子节点不存储真实数据行,只存主键索引和 6 字节指针。
    • 设主键为 BIGINT (8B),指针 6B,一个 16KB 的数据页能容纳 16384/(8+6)≈1170 个分支指针!
    • 高度为 3 的 B+ 树就能索引 1170×1170×16≈21,900,000 条记录!千万级数据仅需 2~3 次磁盘 I/O。
  2. 底层双向有序链表,让范围查询(BETWEEN / > / < / ORDER BY)彻底起飞:
    • 在 B 树中做范围查询必须在整棵树的各个高度间来回回溯(中序遍历);
    • 而在 B+ 树中,仅需树上走访一次定位到下界叶子页,接下来沿着底部双向链表顺藤摸瓜顺序读,天然利用磁盘顺序预读能力(Read-Ahead),极大减少随机 I/O!

5. 生产级选型:B+ 树 vs LSM-Tree ​

维度B+ 树 (InnoDB / PostgreSQL)LSM-Tree (RocksDB / ClickHouse / HBase)
读性能⭐⭐⭐⭐⭐ O(log⁡N) 点查极快,读路径确定⭐⭐⭐ 读放大(需并发查 MemTable + 多层 SSTable + 布隆过滤器)
写性能⭐⭐⭐ 随机写导致页分裂与磁盘随机写入⭐⭐⭐⭐⭐ 顺序追加写极快(全写内存 MemTable + 顺序 WAL 日志)
写放大相对较高(修改 1 个字节也需刷回 16KB 脏页)异步 Compaction 会带来写放大,但被平摊在后台
适用场景传统事务 OLTP、关系型核心数据库、强一致点查海量日志写入、时序监控、大数据指标追加采集

学思并济 · 躬行求索 | Released under MIT License