阅读 507

学过了 B 树再看看 B+ 树吧

前言

学习 B+ 树之前,要先了解 B 树 的基本知识。

认识 B+ 树

B+ 树是 B 树的变形,如果理解了 B 树,B+ 树是很容易理解的。B+ 树与 B 树相比,有如下不同:

  • 叶子节点存储数据,非叶子节点只存储索引
  • 父节点的信息包含在子节点中
  • 叶子节点构成一个链表

如下图所示是一棵 3 阶 B+ 树。

image.png

灰色节点表示索引节点,蓝色节点表示叶子节点,红色表示叶子节点存储的数据。非叶子节点的索引值取的是其 右侧子树中叶子节点的最小值 ,所以同意可以根据索引值划分的范围找到对应的叶子节点。

比如,对于 [48,54] 这个节点,48 是中间孩子节点的最小值,54 是最右侧孩子节点的最小值。

B+ 树的插入

B+ 树的插入和 B 树是一致的,唯一的区别在于:当叶子节点发生上溢时,节点中向上合并的值仍然要保留在叶子节点中

我们依次插入 [24 26 30 36 40 43 45 48 54 56] ,构建一棵 3 阶 B+ 树,过程如下:

  1. 首先插入 24,26

  1. 插入 30,此时节点上溢;将中间的 26 向上分离作为索引节点,同时 26 要和 30 一起保留在叶子节点中。

  1. 插入 36,36 > 26,插入到 [26,30] 中;此时 [26,30,36] 上溢;将中间的 30 向上加入索引节点,同时 30 也要和 36 一起保留在叶子节点中。

  1. 插入 40,40 > 30,插入到 [30,36] 中;此时 [30,36,40] 上溢,调整完成后,父节点 [26,30,36] 也发生了上溢,将中间的 30 向上合并,由于是索引节点,不存储数据,上溢调整和 B 树一样。

  1. 依此类推,插入剩余节点,就可以得到最终的 B+ 树。

B+ 树的删除

B+ 树的删除操作,同样和 B+ 树一致,只不过要注意 维护索引的变化

以下图所示的 3 阶 B+ 树为例,我们依次删除 54、48、46,分别对应了 B 树删除的三种情况:

  1. 情况 1 :删除 54,不会导致下溢,直接删除,由于 54 是叶子节点中最小的元素,所以还要更新父节点是索引为 56。(如果删除的是 56 ,就不需要修改索引)

  1. 情况 2 :删除 48,节点下溢,此时左兄弟节点够借,向兄弟节点借一个元素 46,并更新父节点的索引为 46。

  1. 情况 3 :删除 46,节点下溢,并且没有相邻兄弟节点可借元素;此时,将父节点的元素向下合并为一个新的节点。这种情况可能导致父节点一直下溢,直到树的高度减 1 。

对于情况 3 ,我们也可也换一个角度来看:我们可以先将删除的节点和左(右)兄弟合并,然后再删除。


下面我们就用先合并再删除的方法演示一下删除以及连续下溢的情况。对上面删除的结果,继续删除 45,过程如下(为了让图片看的更清楚,我们省略了索引节点 30 的子树部分):

  1. 首先将 45 和 46 合并,由于只有一个子节点,父节点可以认为有 0 个元素;合并后再直接删除 45,并修改索引节点 45 为 56。

  1. 删除 45 后,父节点由于没有元素也发生了下溢,将父节点 56 向下和 43 合并为一个节点。

  1. 此时,节点 [43,56] 的父节点又发了下溢,继续将根节点 40 向下和 30 合并为一个节点,树的高度减 1 。

B+ 树的搜索

树形搜索

B+ 树数据都存储在叶子节点中,所以按照树的搜索方式,时间复杂度总是 O(logn) ,而 B 树最好情况可以达到 O(1)

链表搜索

B+ 树也可以利用叶子节点构成的链表进行搜索,先找到最左下的叶子节点,然后在链表中顺序搜索。

范围搜索

利用叶子节点链表,B+ 树(与 B 树相比)还可以非常容易地实现 范围搜索

比如要查找 30~43 的数据,先找到 30,然后在链表中顺序搜索小于 43 的元素即可。而对于 B 树来说(如下图),查找到 30 后,还需要不断向上层回溯找到它的后继 36,直到找到 43 。

换个角度理解 B+ 树

为了更好的理解 B+ 树的特点,我们利用 3 阶 B 树稍作变化就可以得到 B+ 树。比如按照 [24,26,30,36,40,43,45,48,54] 的顺序构造一棵 B 树,结果如下:

我们将非叶子节点看作索引节点,标为灰色,并且将对应的元素值加到左侧子树的叶子节点中(比如 26 加到 30 中构成 [26,30] 节点,43 加到 45 中,48 加到 54 中,36 加到 40 中),最后将叶子节点连成一个链表,就得到了一棵 B+ 树。、

image.png

但要注意,这个 B+ 树并不是按照 B 树的插入顺序得到,而是按照 [54 48 45 36 30 26 24 43 40] 的顺序构建的。


我查阅了很多资料,发现关于 m 阶 B+ 树的节点元素个数是有争论的

  • 第一种:最多 m-1(本文采用)
  • 第二种:最多 m

对于第二种表示方法,每个索引值对应一个子节点,索引值是子节点中的最大值。下图所示是用第二种表示的 3 阶 B+ 树。

image.png

无论是哪一种,B+ 树的核心都是一致的,无非是索引范围的划分不一样。从 B 树向 B+ 树转化的角度来看,我个人认为第一种与 B 树更相似,更容易理解,因此本文也采用的是第一种表示。

B+ 树和 B 树

B+ 树与 B 树有如下区别:

  • 单个节点能存储更多元素,可以减少 IO 次数,MySQL 用 B+ 树建立索引
  • 查询性能稳定,为 O(logn)
  • 叶子节点存储数据,非叶节点只表示索引,并且叶子节点构成链表
  • 范围查找很方便

总结

本文介绍了 B+ 树的基本概念,B+ 树广泛用于数据库索引中,比如 MySQL 就是采用 B+ 树。以后会继续介绍 B+ 树在 MySQL 索引中的使用以及 MySQL 的相关知识。

文章分类
后端