B+树要点梳理

· 浏览次数 : 16

小编点评

B+树是一种自平衡二叉查找树,在数据库和文件系统中广泛使用。本文主要讨论B+树的重要操作,包括中间节点的key和指针原则、分裂、合并、删除等。 1. 中间节点中间节点的key与其对应的指针原则 - 小于key的元素在其指针指向的节点中 - 大于key的节点由key对应的指针指向 - 将left_count对应的key作为upper_key 2. 分裂 - 当中间节点数量满时,进行分裂 - 新生成一个相邻的中间节点right_internal_node - 计算原先保留多少元素,例如保留left_count = K/2 - 将left_count对应的key复制到新生成的节点 - 将left_count + 1下标对应的指针留在左节点 - 将left_count + 1下标对应的指针开始复制到新生成的节点中 - 分裂后插入上层中间节点 3. 合并 - 当节点确实从左右都没有节点可以借入数据时,与左右节点合并 - 选择与其相同父节点下的相邻节点进行合并 - 节点合并后,需要使父节点删除掉被合并数据的节点 4. 删除 - 删除后,尝试从其右侧的节点借数据 - 如果节点最左边一个元素被删除了,则需要更新中间对应的key - 借入数据的流程与中间节点类似 5. 叶子节点插入分裂 - 当叶子节点数量满了时,进行分裂 - 新生成一个相邻sibling节点 - 将下标left_count开始的key移动到新生成的节点 - 将left_count对应的key作为upper_key - 将下标left_count开始的value移动到新生成的节点 - 分裂后插入上层中间节点 总结:B+树的重要操作包括中间节点的key和指针原则、分裂、合并、删除等。这些操作保证了B+树的平衡性和高效性。在实际应用中,可以根据具体需求选择合适的实现方式。

正文

B+树重要操作

中间节点

  • 中间节点的key,与其对应的指针的原则是,小于key的元素在其指针指向的节点中
  • 中间节点的key可以看成是右斜着排放的,即小于等于key的节点由key对应的指针指定,最有一个指针指向大于最右侧key的节点

分裂

  • 当中间节点数量满了时,进行分裂,新生成一个相邻的中间节点right_internal_node
  • 计算原先保留多少元素, 例如保留left_count = K/2
  • 将left_count对应的key作为upper_key后续插入到中间节点
  • 从left_count + 1下标开始,将key复制到新生成的节点中
  • 将left_count下表对应的指针留在左节点
    • 将left_count +1 下表对应的指针开始复制到新生成的节点中

分裂后插入上层中间节点

  • 经过分裂有三个重要元素:left_internal_node(也就是原先被分裂节点), right_internal_node(新生成的节点),upper_key
  • 上层中间节点为upper_internal_node
  • 注意原先upper_internal_node有一个old_key和old_ptr指针指向left_internal_node,这个old_key肯定是比upper_key大的
    • 将(upper_key, left_internal_node)作为一对插入到upper_internal_node
    • 将old_key对应的old_ptr改成指向right_internal_node

image

删除

借入数据

中间节点删除数据后,如果数量少于最低值K/2,那么它可以尝试从它的相邻节点借入数据。那么它的相邻节点在哪里呢?第一类相邻节点是与中间节点共享一个父节点的节点,很容易通过其父节点找到。第二类相邻节点,不与其共享一个父节点:想象一下当节点是父节点的最左边节点时,那么在其父节点下,它只有一个右侧相邻节点,这个时候它的左侧相邻节点不在其父节点下;当节点是父节点的最右边节点时,那么在其父节点下,它只有一个左侧相邻节点,这时候它的右侧相邻节点不在其父节点下,而在右侧树分支下。

image

  • 注意第二类的借入在实现的时候也可以不考虑,只不过这样会过早的带来节点合并。

节点合并

当节点确实从左右都没有节点可以借入数据时,那么它就可以与左右节点合并了。正因为其左右节点也没有多的节点可以借,说明其左右节点不大于K/2,那么合并后节点节点数量是会小于K的。

节点合并,我们可以总是选择与其相同父节点下的相邻节点进行合并。节点合并后,需要使父节点删除掉被合并数据的节点(需要注意的是在实际操作中,我们可以选择被删除的节点最终保留下来,也可以选择相邻节点最终保留下来,这只是实现方式)。

降级

父节点删除某个子节点后,需要检查父节点的key数量是否满足K/2要求,如果父节点不满足,那么父节点也需要执行借入动作,如果父节点无法接入,那么就需要执行合并动作。并最终出发祖父节点的删除动作。整个流程会递归上去最终可能会到根节点,若根节点删除了数据,且一个key也没有了。那么就要将其子节点设置成新的root了(奇怪?没有key还有子节点?注意中间节点指针比key多一个,就是说没有key,也至少有一个指针)。

叶子节点

插入

分裂

  • 当叶子节点数量满了时,进行分裂,新生成一个相邻sibling节点
  • 计算原先保留多少元素, 例如保留left_count = K/2
  • 将下标left_count开始的key移动到新生成的节点
    • 将left_count对应的key作为upper_key后续插入到中间节点
  • 将下标left_count开始的value移动到新生成的节点

分裂后插入上层中间节点

  • 新分裂出的节点的第一个key为upper_key
  • 如果叶子节点没有上层中间节点,那么就新建一个中间节点作为根节点,把原先叶子节点以及新分裂出的叶子节点直接插入到新的中间节点。同时将upper_key插入到新中间节点中。即它有1个key,两个指针。
  • 如果有上层中间节点
    • 首先将upper_key插入到中间节点
    • 其次将新分裂出的节点指针也插入到中间节点,这里需要注意的是由于中间节点的指针数量比key数量多一个。所以指针插入的index要比upper_key插入的index大1
      image

删除

  • 删除后,尝试从其右侧的节点借数据,前提是右侧节点包含m/2 +1个元素
  • 若节点(且该节点不是中间节点的最左侧节点)最左边一个元素被删除了,则需要更新中间对应的key

借入数据

借入数据的流程是简单的,与中间节点类似。找到其相邻节点并拿到数据。同样需要注意的是它的相邻节点也可能不在其父节点下。

节点合并

与中间节点的操作类似,找到相邻节点,拷贝数据。通知父节点删除一个节点即可。

要记录哪些指针

  • 从B+树的定义上来看,至少每个叶子节点需要记录一个指向右侧相邻节点的指针

其他指针要不要记录呢?

  • 节点是否要记录父节点指针?
  • 中间节点是否要记录右侧节点的指针?
  • 节点是否要记录其左侧节点的指针?

这个因设计实现而异了,记录更多的指针,那么在寻找定位的时候肯定更加容易,但是也带来了很多维护成本,这个在设计的时候做考量。

如何找到相邻的节点

相同父节点下的相邻节点

很容易,很自然,进行遍历就可以或者对key进行find或二分法(注意所有节点里面的key都是有序的)

非相同父节点下的相邻节点

  • 最节点的方式是记录节点(包括中间节点)的左侧、右侧指针

另外一种方式,如果不想过多的记录指针,那么可以通过搜索回溯的方式来找目标节点到相邻节点。 某个最左侧中间节点为例:

  • 从它开始往上回溯,当回溯到的节点不是其父节点的最左侧节点时,说明我们已经找到了共同祖先
  • 然后从共同祖先中的左侧节点(对应下图1的左侧节点3)开始往下找其最右侧的子节点,当往下搜索的层数与向上回溯的层数相同时,我们就找到了目标节点的相邻节点。
  • 最右侧中间节点的搜索过程是类似的。
    image

例如想找到节点5的做相邻节点4,就往上回溯首先到1, 5是1的最左侧节点,则继续向上到2,1不是2的最左侧节点,所有2就是共同祖先了。1的左侧节点3往下,找到最右侧节点4, 4与5是相同层级的,那么就是5的相邻左节点了。

另外一个问题是如何进行回溯呢?一种实现方式是子节点记录父节点的指针;另一种实现方式是,记录搜索过程的搜索栈(即从根节点到叶子节点经过了哪些中间节点,记录到一个列表里面)。

与B+树要点梳理相似的内容:

B+树要点梳理

B+树重要操作 中间节点 中间节点的key,与其对应的指针的原则是,小于key的元素在其指针指向的节点中 中间节点的key可以看成是右斜着排放的,即小于等于key的节点由key对应的指针指定,最有一个指针指向大于最右侧key的节点 分裂 当中间节点数量满了时,进行分裂,新生成一个相邻的中间节点rig

[转帖]深入理解mysql-第六章 mysql存储引擎InnoDB的索引-B+树索引

一、引入索引 在没有索引的情况下,不论是根据主键列或者其他列的值进行查找,由于我们并不能快速的定位到记录所在的页,所以只能从第一个页沿着双向链表一直往下找,因为要遍历所有的数据页,时间复杂度就是O(n),所以这种方式显然是超级耗时的。所以我们需要采取一定的数据结构来存储数据,方便我们进行数据的增删改

图解B树及C#实现(1)

前言 B树(B-tree),也常被记作 B-树,其中“-”不发音。B树的发明者 Rudolf Bayer 和 Edward M. McCreight 并没有给B树中的 B 明确的定义,大家也不必对此纠结太多。 B+树是B树的变体,两者的适用场景是不一样的,以后也会给大家带来B+树的介绍。 本系列将用

图解B树及C#实现(2)数据的读取及遍历

前言 本文为系列文章 B树的定义及数据的插入 数据的读取及遍历(本文) 数据的删除 前一篇文章为大家介绍了 B树 的基本概念及其插入算法。本文将基于前一篇的内容,为大家介绍插入到 B树 中的数据该怎么读取及遍历, 本文的代码基于前一篇文章的代码,已经实现的功能可能会被省略,只介绍新增的功能。 在本文

图解B树及C#实现(3)数据的删除

前言 本文为系列文章 B树的定义及数据的插入 数据的读取及遍历 数据的删除 阅读本文前,建议先复习前两篇文章,以便更好的理解本文。 从删除的数据所在的节点可分为两种情况: 从叶子节点删除数据 从非叶子节点删除数据 无论从叶子节点还是非叶子节点删除数据时都需要保证B树的特性:非根节点每个节点的 key

MongoDB 中的索引分析

MongoDB 的索引 前言 MongoDB 使用 B 树还是 B+ 树索引 单键索引 创建单键索引 使用 expireAfterSeconds 创建 TTL 索引 复合索引 最左匹配原则 ESR 规则 如何使用排序条件 多键索引 创建多键索引 局限性 哈希索引 注意事项 创建索引 总结 参考 Mo

[转帖]LSM树详解

https://zhuanlan.zhihu.com/p/181498475 LSM树(Log-Structured-Merge-Tree)的名字往往会给初识者一个错误的印象,事实上,LSM树并不像B+树、红黑树一样是一颗严格的树状数据结构,它其实是一种存储结构,目前HBase,LevelDB,Ro

咬文嚼图式的介绍二叉树、B树/B-树

网上的很多博客都是只有文字说明,比较抽象,所以笔者决定自己画一些图来解释二叉树,二叉搜索树,B树/B-树。

MySQL面试题全解析:准备面试所需的关键知识点和实战经验

本次种子题目主要涵盖了MySQL的存储引擎和索引结构,如B+树索引和哈希索引,以及覆盖索引和回表的概念。此外,还包含了MySQL事务的ACID特性和隔离级别。另外,对MySQL主从集群中的binlog日志的执行顺序和作用进行了讨论。最后,还涉及了分库分表和读写分离的概念。这些内容涵盖了MySQL数据库的核心知识和重要技术,不仅在面试中起到关键作用还对于优化数据库性能和应用开发都具有重要意义。

【转帖】MySQL InnoDB存储原理深入剖析与技术分析

一、MySQL记录存储: MySQL InnoDB的数据由B+树来组织,数据记录存储在B+树数据页(page)中,每个数据页16kb,数据页 包括页头、虚记录、记录堆、自由空间链表、未分配空间、slot区、页尾七部分组成。 所有的非终端结点可以看成是索引部分,结点中仅含有其子树根结点中最大(或最小)