B树 vs B+树:深入解析数据库索引的核心差异

80 阅读4分钟

在数据库系统和文件系统中,B树(B-Tree)和B+树(B+Tree) 是最广泛使用的多路平衡搜索树结构。它们能高效管理磁盘数据访问,减少I/O操作。尽管名字相似,二者在结构和性能特性上存在关键差异。本文将深入对比其设计原理、数据存储方式和适用场景。


一、核心结构对比

1. B树(B-Tree)

  • 数据存储:
    所有节点(包括内部节点和叶子节点)均存储完整的键值对(Key-Value) 数据。
  • 指针结构:
    每个节点中的键值对之间会存储指向子节点的指针。

2. B+树(B+Tree)

  • 数据存储:

    • 内部节点:仅存储键(Key) 和指向子节点的指针,不存储实际数据。
    • 叶子节点:存储完整的键值对,并通过双向链表横向连接。
  • 指针结构:
    内部节点中的键只作为路由索引,叶子节点包含所有有效数据。


二、关键差异详解

特性B树B+树
数据位置所有节点均存数据仅叶子节点存数据
叶子节点连接叶子节点无链表连接叶子节点通过双向链表连接
查询性能随机查询稳定(可提前终止)稳定O(log n),必须到叶子节点
范围查询需遍历多级节点(效率低)双向链表支持高效顺序遍历
存储利用率节点大小包含数据,利用率低内部节点仅存键,可存储更多索引
树高度相对较高(节点含数据)更矮(内部节点纯索引)
删除操作可能触发节点合并(复杂)数据只在叶子节点,删除更简单

三、操作流程

查询 Key=10​

  • B树:
    若在内部节点找到Key=10​,直接返回数据(无需到叶子层)。
  • B+树:
    必须逐层下钻到叶子节点才能获取数据。

范围查询 Key ∈ [5, 20]​

  • B树:
    需递归遍历多个子树,I/O路径复杂。
  • B+树:
    定位到Key=5​的叶子节点,沿链表顺序扫描至Key=20​。

在已满节点插入 Key=28​

  • B树:
  1. 定位叶子节点
  2. 节点分裂:
    [20,25,30] → 分裂为 [20] 和 [28,30]
  3. 提升中间键(含数据)到父节点:
    提升25到父节点
    ❗ 数据上移到内部节点
  • B+树:
  1. 定位叶子节点
  2. 节点分裂:
    [20,25,30] → 分裂为 [20] 和 [25,28,30]
  3. 提升副本键(不含数据)到父节点:
    提升25(作为索引)到父节点
    ✅ 数据保留在叶子层

四、为什么数据库偏爱B+树?

  1. 更优的磁盘I/O优化

    • 内部节点不存数据 → 单节点可容纳更多键 → 树高度降低 → 减少磁盘寻道次数。
    • 叶子节点链表结构天然适合范围扫描(常见于SQL查询)。
  2. 更高的缓存利用率
    内部节点(纯索引)可全部载入内存,仅需1次磁盘访问叶子层数据。

  3. 稳定的查询延迟
    所有查询均到叶子层结束,执行时间可预测。

  4. 全盘扫描高效
    沿叶子链表遍历即可获取全量数据,无需树回溯。


五、B树的使用场景

尽管B+树主导磁盘数据库,B树仍有适用场景:

  1. 内存数据库(如Redis):
    数据在内存中,无磁盘I/O压力,B树随机访问快的优势更明显。
  2. 写密集型场景:
    B树的数据分散存储,写入局部性更好(但需权衡查询代价)。
  3. 唯一键值访问为主:
    若极少范围查询,B树的提前返回特性可能更优。

总结

场景推荐结构原因
磁盘数据库索引✅ B+树减少I/O,优化范围查询
内存数据存储⚡ B树随机访问快,避免冗余遍历
高频点查+低频范围查询🟡 视情况选择根据读写比例权衡

简而言之,B树是"分散式存储"(数据遍布所有节点),B+树是"分层式存储"(数据在叶子层集中存储+索引分层)。