引言

MySQL的InnoDB引擎使用B+树作为索引的数据结构。理解B+树是掌握索引优化的基础。


一、从二叉树到B+树的演进

1.1 二叉查找树(BST)

     10
    /  \
   5    15
  / \   / \
 3   7 12 20

问题:可能退化为链表,查询O(n)

1.2 平衡二叉树(AVL)

通过旋转保持平衡,查询O(log n)。

问题

  • 每个节点只存1个元素
  • 树高度大:100万数据需要20层
  • 磁盘IO次数多(每层一次IO)

1.3 B树

         [10, 20, 30]
        /   |   |   \
    [1,5] [12,15] [22,25] [35,40]

特点

  • 多路:每个节点存多个元素
  • 所有节点都存数据
  • 降低树高度

问题

  • 非叶子节点也存数据,占用空间
  • 范围查询需要中序遍历

1.4 B+树

非叶子节点(只存索引):
         [10, 20, 30]
        /   |   |   \
       /    |   |    \
叶子节点(存数据+索引):
[1,5,8] → [10,12,15] → [20,22,25] → [30,35,40]
   ↑ 双向链表 ↑

特点

  1. 非叶子节点只存索引
  2. 所有数据在叶子节点
  3. 叶子节点形成有序链表

二、B+树的核心特性

2.1 多路平衡

  • 每个节点可存储多个键值
  • InnoDB:16KB页,约1200个指针
  • 3层B+树可存:1200 × 1200 × 1200 ≈ 17亿条记录

2.2 所有数据在叶子节点

非叶子节点:[5, 10, 15](只存索引,不存数据)
              ↓
叶子节点:[1→data, 5→data, 8→data](存完整数据)

优势

  • 非叶子节点更小,一次IO读更多索引
  • 降低树高度

2.3 叶子节点链表

[1,2,3] ⇄ [5,6,7] ⇄ [10,11,12] ⇄ [15,16,17]

优势

  • 范围查询快(顺序扫描链表)
  • 全表扫描快

三、为什么MySQL选择B+树?

3.1 vs 二叉树

100万数据:
- 二叉树:高度20,需要20次磁盘IO
- B+树:高度3,需要3次磁盘IO

3.2 vs 哈希

-- 哈希:等值查询快
WHERE id = 100;  -- O(1)

-- 范围查询慢
WHERE id BETWEEN 100 AND 200;  -- 无序,需要全扫描

-- B+树:范围查询快(有序)
WHERE id BETWEEN 100 AND 200;  -- 顺序扫描

3.3 vs B树

对比项B树B+树
数据存储所有节点只在叶子节点
非叶子节点存数据+索引只存索引
一次IO读取
范围查询中序遍历叶子链表
全表扫描

四、B+树的查询过程

4.1 单值查询

SELECT * FROM users WHERE id = 15;

查询过程

  1. 从根节点开始:[5, 10, 15, 20]
  2. 15在[15, 20]区间,走第3个指针
  3. 到达叶子节点:[15→data]
  4. 返回数据

IO次数:3次(3层树)

4.2 范围查询

SELECT * FROM users WHERE id BETWEEN 10 AND 20;

查询过程

  1. 找到起始位置(id=10)
  2. 沿着叶子节点链表顺序扫描
  3. 直到id=20

IO次数:3次(定位) + N次(扫描N个数据页)


五、B+树的插入和删除

5.1 插入

INSERT INTO users (id, name) VALUES (12, '张三');

过程

  1. 找到叶子节点位置
  2. 插入数据
  3. 如果节点满,触发页分裂

页分裂

  • 节点满时,分裂为两个节点
  • 中间键提升到父节点
  • 影响性能

5.2 删除

DELETE FROM users WHERE id = 12;

过程

  1. 找到数据位置
  2. 删除数据
  3. 如果节点过空,触发页合并

六、B+树高度分析

6.1 计算公式

层数 = ceil(log_扇出(记录数))

扇出 ≈ 页大小 / (键大小 + 指针大小)
     ≈ 16KB / (8B + 6B)
     ≈ 1200

6.2 容量估算

1层:1200条
2层:1200 × 1200 = 144万条
3层:1200 × 1200 × 1200 = 17亿条

结论:3层B+树足够存储千万到亿级数据。


七、实战分析

7.1 查看索引信息

-- 查看表的索引
SHOW INDEX FROM users;

-- 查看索引统计信息
SELECT
    index_name,
    stat_name,
    stat_value
FROM mysql.innodb_index_stats
WHERE table_name = 'users';

7.2 估算索引高度

-- 计算表的索引高度(近似)
SELECT
    CEIL(LOG(1200, COUNT(*))) AS estimated_height
FROM users;

八、总结

核心要点

  1. B+树特点:多路、平衡、叶子节点链表
  2. 优势:降低树高度、减少IO、范围查询快
  3. 容量:3层可存17亿数据
  4. 查询:等值O(log n)、范围扫描链表
  5. 维护:插入/删除可能触发页分裂/合并

记忆口诀

B+树多路又平衡,叶子节点存数据项,
非叶只存索引值,链表连接顺序访。
三层可存十亿量,磁盘IO三次访,
范围查询特别快,MySQL选它有道理。

本文字数:约2,300字 难度等级:⭐⭐⭐⭐(索引进阶)