龙空技术网

MySQL中的B+树到底是个什么样的结构?

从程序员到架构师 266

前言:

当前咱们对“mysql 树结构”大约比较注意,朋友们都想要学习一些“mysql 树结构”的相关知识。那么小编同时在网上收集了一些关于“mysql 树结构””的相关文章,希望各位老铁们能喜欢,各位老铁们快快来了解一下吧!

B+树和二叉树、平衡二叉树一样,属于一个比较经典的数据结构。B+树是由B树加上索引顺序访问方法演化而来,索引顺序访问又被称为是ISAM,也就是MyISAM引擎最初的数据结构。但是随着不断地演化发展现在基本上已经看不到B树的结构了。下面我们就来看看在MySQL中的B+树到底是个什么东西

B+树

对于B+树定义在很多的资料中都可以找到,其定义非常的复杂,这里我们将其定义做了一个精简。

在MySQL中使用B+树是为磁盘或者是其他的直接辅助存储设备所设计的一种平衡的查找树结构。也就是说在B+树中,记录的所有的节点数据都是按照键值大小顺序存放到同一层的叶子结点上,并且由叶子结点之间的指针来完成数据的链接。如下图所示。

图中展示了一个高度为2的B+树结构,由4个叶子节点,并且所有的记录都在叶子节点上按照顺序存放,也就是说用户可以从左边的叶子节点开始按照顺序遍历所有的键值。

这样我们就可以理解什么是B+树,至于B+树有哪些性质,这里我们不做展开的讨论。有兴趣的读者可以自己研究一下。

在MySQL对B+树的应用中,主要是对数据的增删改查。下面我们就来看一下如何实现这些功能。

B+树的插入操作

B+树在插入数据之后,必须要保证叶子节点中的数据记录依然是有序的,所以,在插入的时候需要考虑到如下的几种情况。

Leaf Page 未满,Index Page未满

这种情况下,我们可以直接对数据进行插入到叶子节点中。

LeafPage 满,Index Page 未满

第一步、先要对Leaf节点进行拆分,并且将中间节点放入到Index Page中。

第二步、将小于中间节点的数据放入到左边的叶子节点中,将大于中间节点的数据放在右边的叶子节点中。

LeafPage 满,Index Page满

第一步、先去拆分Leaf Page,将小于中间节点的数据放入左边,将大于中间节点的数据放入右边。

第二步、拆分Index Page,将小于中间节点的数据放入到左边,将大于中间节点的数据放入右边。

第三步、由于上面两步操作会出现一个新的IndexPage中间节点,那么最终将这个节点放入到上一层的IndexPage中。

如图所示,当有28这个数据想要插入到这个B+树中的时候,发现属于Leaf Page和Index Page都未满的状态,那么直接插入即可。

如果将70这个数据插入到这个B+树的时候,那么这个时候会发现对应的Leaf Page 已经满了,这种情况就属于上面的第二种情况,那么插入之后叶子节点将变成 50、55、60 、65 、70。这个时候需要根据规则来进行Leaf Page的拆分。如下图所示。将60升级为Index Page中的节点,然后将70这个数据插入到其中。

那么再插入95这个数据,这个时候会发现属于第三种情况Index Page 和 Leaf Page都满了。这个时候需要进行二次拆分,先去拆分叶子节点将中间数据85提取出来然后升级为Index Page中的数据,然后继续将Index Page进行拆分将中间节点60提取出来升级到上一层上,最终会形成如下的一个数据结构。

会看到,无论B+树中的数据如何变化,最终B+树都会保持平衡,但是为了保证平衡,插入数据对于B+树来讲可能要完成大量的数据拆分平衡等操作。由于B+树结构主要是用于磁盘存储,所以页拆分操作就意味着需要对磁盘进行操作,对磁盘进行操作的时候对于I/O的效率会有很大的影响,所以要尽量减少页拆分操作。

为了解决这个问题,B+树提供了类似于平衡二叉树一样的旋转操作。

B+树的旋转操作

B+树的旋转操作发生在Leaf Page已满,但是其左边的兄弟节点未满的情况下。这个时候B+树不会先去考虑进行拆分,而是将记录移动到所在页的兄弟节点上,也就是说先实现与兄弟节点之间的数据平衡操作。

通常情况下,左边的兄弟节点会先来进行旋转检查操作,如下图所示。

这个时候我们需要插入的值依然是70,这个时候发现叶子节点满了,这个时候需要考虑其左边的叶子节点,发现左边的叶子节点中还有一个空位置,那么这个时候,需要做的操作就是将50这个元素移动到左边的叶子节点上,然后将70这个数据插入到右边节点上,这个时候整个树结构还是两层,如下图所示。

B+树的删除数据操作

介绍完B+树数据插入操作之后,下面我们来看看如何从一个B+树中删除一个元素。

在B+树中使用所谓的 fill factor 填充因子来控制树的删除变化,50%为B+树可以设置的最小填充因子的值。同样B+树在完成数据删除操作之后依然要保证删除之后的所有叶子结点数据都是有序的。同样我们也需要分如下的几种情况来考虑

叶子节点大于填充因子,中间节点大于填充因子

这种情况下,可以直接删除叶子节点,如果该节点还是Index Page中的节点,那么用该节点的右边节点去代替。

叶子结点小于填充因子,中间节点大于填充因子

这种情况下,需要合并叶子节点以及其他的兄弟节点数据,同时需要更新IndePage的节点顺序

叶子节点小于填充因子,中间节点小于填充因子

第一步、需要合并叶子节点以及兄弟节点的数据,然后更新Index Page的数据。

第二步,如果IndexPage节点情况相同,则需要合并Index Page的节点以及其兄弟节点的数据。

这里我们尝试删除上面的70这条数据。

这个时候发现70这个数据与上面描述的第一种情况,也就是删除之后不会对B+树的结构产生影响,并且叶子节点本身有4个数据删除70之后,还有两个满足50%的装填因子,所以直接删除就可以了。

尝试删除25这个数据,会发现删除完成之后,25所对应的叶子结点,会因为删除25这个元素之后,Index Page 小于了2个,这个时候,需要更新Index Page,需要将Leaf Page节点上的数据进行合并,更新到Index Page上。删除25之后28作为该叶子节点上的第一个节点元素,并且叶子节点有2个数据,所以不做其他的操作结构如下。

下面我们来看一个比较复杂的数据删除,删除60这个元素。删除60之后,会影响到整体装填度。也就是说需要大面积的节点合并。由于删除的元素在Index Page中,所有IndexPage节点需要进行合并。然后发现,删除了60这个节点之后,所在的子节点的元素小于了2个。这个时候需要对其对应的兄弟节点进行合并,合并完成之后如下图所示。

总结

对于B+操作,还是比较复杂的,上面我们介绍了如何去添加元素、如何去保证树平衡、如何删除元素、如何删除元素之后继续保证树结构的平衡。

标签: #mysql 树结构