一次聊个痛快,B-,B+树的特点和使用场景

Posted 健康平安的活着

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一次聊个痛快,B-,B+树的特点和使用场景相关的知识,希望对你有一定的参考价值。

一 B树

1.1 B树

1.B树的原英文名称为B-TREE,而国内很多人喜欢B-Tree译作B-树,其实,这是个很不好的直译,很容易让人产生误解。如人们可能会以为B树是一种树,而B-树又是一种树,而事实上,B-Tree就是指的是B树,平衡多路查找树(B-Tree),目前B的意思理解为平衡。B 树就是常说的“B 减树(B- 树)”,又名平衡多路(即不止两个子树)查找树。

2.二叉树阶M:  树中所有孩子结点个数的最大值称为该树的阶!树的阶数表示一个节点最多能有多少个子节点,也就是每个节点上最多的键值个数。比如二叉树的阶数就是2

B树中的一个节点的子节点数目的最大值,用m表示,假如最大值为5,则为5阶,如图:

 所有节点中,节点【13,16,19】拥有的子节点数目最多,四个子节点(灰色节点),所以可以定义为上面的树为4阶B树。

3.B 树的定义,一棵 B 树必须满足以下条件

   1.若根节点不是终端节点,则至少有2棵子树。

    2.除根节点以外的所有非叶节点至少有M/2(注意向上取整)棵子树,至多有M个子树(关键字个数为子树个数-1,M为为节点的阶数)。也就是说节点存储n个数,孩子得有n+1个,“子树个数是关键字个数+1

    3.所有的叶子节点都位于同一层。

   4.B-树的节点数据大小也是按照左小右大,子树与节点的大小比较决定了子树指针所处位

 1.3 B树于平衡二叉树的区别联系

B 树就是常说的“B 减树(B- 树)”,又名平衡多路(即不止两个子树)查找树。它和平衡二叉树的不同有这么几点:

  1. 平衡二叉树节点最多有两个子树,而 B 树每个节点可以有多个子树,M 阶 B 树表示该树每个节点最多有 M 个子树。(即一个节点最多有M个子节点,表示其是M阶树)。

      平衡二叉树的节点如下图所示,每个节点有一个数据和最多两个子树

B 树中的每个节点由两部分组成:

  1. 关键字(可以理解为数据)
  2. 指向孩子节点的指针

B 树的节点如下图所示,每个节点可以有不只一个数据,同时拥有数据数加一个子树,同时每个节点左子树的数据比当前节点都小、右子树的数据都比当前节点的数据大:

2.平衡二叉树每个节点只有一个数据和两个指向孩子的指针,而 B 树每个中间节点有 k-1 个关键字(可以理解为数据)和 k 个子树( **k 介于阶数 M 和 M/2 之间,M/2 向上取整)

4 阶 B 树表示每个节点最多有 4 个子树、3 个关键字,最少有 2 个子树、一个关键字

3 阶 B 树表示每个节点最多有 3个子树、2个关键字,最少有 2(3/2向上取整) 个子树、1个关键字

3.B 树的所有叶子节点都在同一层,并且叶子节点只有关键字,指向孩子的指针为 null

相同点:

  1. 和平衡二叉树相同的点在于:B 树的节点数据大小也是按照左小右大,子树与节点的大小比较决定了子树指针所处位置。

可以看到,B 树的每个节点可以表示的信息更多,因此整个树更加“矮胖”,这在从磁盘中查找数据(先读取到内存、后查找)的过程中,可以减少磁盘 IO 的次数,从而提升查找速度。 

1.4 B树的查找过程

B 树的子树大小排序规则,因此在 B 树中查找数据时,一般需要这样:

  1. 从根节点开始,如果查找的数据比根节点小,就去左子树找,否则去右子树
  2. 和子树的多个关键字进行比较,找到它所处的范围,然后去范围对应的子树中继续查找
  3. 以此循环,直到找到或者到叶子节点还没找到为止

3阶的B-Tree的查找过程:

 每个节点占用一个盘块的磁盘空间,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在磁盘块的地址。两个关键词划分成的三个范围域对应三个指针指向的子树的数据的范围域。以根节点为例,关键字为17和35,P1指针指向的子树的数据范围为小于17,P2指针指向的子树的数据范围为17~35,P3指针指向的子树的数据范围为大于35。

模拟查找关键字29的过程:

  1. 根据根节点找到磁盘块1,读入内存。【磁盘I/O操作第1次】
  2. 比较关键字29在区间(17,35),找到磁盘块1的指针P2。
  3. 根据P2指针找到磁盘块3,读入内存。【磁盘I/O操作第2次】
  4. 比较关键字29在区间(26,30),找到磁盘块3的指针P2。
  5. 根据P2指针找到磁盘块8,读入内存。【磁盘I/O操作第3次】
  6. 在磁盘块8中的关键字列表中找到关键字29。

发现需要3次磁盘I/O操作,和3次内存查找操作。由于内存中的关键字是一个有序表结构,可以利用二分法查找提高效率。而3次磁盘I/O操作是影响整个B-Tree查找效率的决定因素。B-Tree相对于AVLTree缩减了节点个数,使每次磁盘I/O取到内存的数据都发挥了作用,从而提高了查询效率。

1.5 B树自平衡过程

平衡的树之所以能够加快查找速度,是因为在添加、删除的时候做了某些操作以保证平衡。平衡二叉树的平衡条件是:左右子树的高度差不大于 1;而 B 树的平衡条件则有三点:

  1. 叶子节点都在同一层
  2. 每个节点的关键字数为子树个数减一(子树个数 k 介于树的阶 M 和它的二分之一
  3. 子树的关键字保证左小右大的顺序

也就是说,一棵 3 阶的 B 树(即节点最多有三个子树),每个节点的关键字数最少为 1,最多为 2,如果要添加数据的子树的关键字数已经是最多,就需要拆分节点,调整树的结构。

我们来分析下 B 树添加元素时如何保证平衡。下用以表示往 4 阶 B 树中依次插入下面这组数据的过程:6 10 4 14 5 11 15 3 2 12 1 7 8 8 6 3 6 21 5 15 15 6 32 23 45 65 7 8 6 5 4

 分析其过程:

我们来根据前几步分析下 B 树的添加流程:

首先明确:4 阶 B 树表示每个节点最多有 4 个子树、3 个关键字,最少有 2 个子树、一个关键字
添加 6,第一个节点,没什么好说的
添加 10,根节点最多能放三个关键字,按顺序添到根节点中
添加 4,还能放到根节点中
添加 14,这时超出了关键字最大限制,需要把 14 添加为子树,同时为了保证“所有叶子节点在同一层”,就需要拆几个关键字作为子树:

拆分后:

 这个拆的过程比较复杂,首先要确定根节点保留几个关键字,由于“非叶子节点的根节点至少有 2 棵子树”的限制,那就至少需要两个关键字分出去,又因为“子树数是关键字数+1”,如果根节点有两个关键字,就得有三个子树,无法满足,所以只好把除 6 以外的三个关键字都拆为子树。

谁和谁在一个子树上呢,根据“左子树比关键字小、右子树比关键字大”的规律,4 在左子树,10 和 14 在右子树。

继续添加 :

  1. 添加 5,放到 4 所在的子树上
  2. 添加 11,放在 10 和 14 所在的右子树上
  3. 添加 15,按大小应该放到 10、11 和 14 所在的子树上,但因为超过了关键字数限制,又得拆分

因为“根节点必须都在同一层”,因此我们不能给现有的左右子树添加子树,只能添加给 6 了;但是如果 6 有三个子树,就必须得有 2 个关键字,提升谁做关键字好呢,这得看谁做 6 中间的子树,因为右子树的所有关键字都得比父节点的关键字大,所以这个提升的关键字只能比未来右子树中的关键字都小,那就只有 10 和 11 可以考虑了。

提升 10 吧,没有比它小的做子树,那就只能提升 11 了:

再添加元素也是类似的逻辑:

首先考虑要插入的子树是否已经超出了关键字数的限制
超出的话,如果要插入的位置是叶子节点,就只能拆一个关键字添加到要插入位置的父节点
如果非叶子节点,就得从其他子树拆子树给新插入的元素做孩子

删除也是一样的,要考虑删除孩子后,父节点是否还满足子树 k 介于 M/2 和 M 的条件,不满足就得从别的节点拆子树甚至修改相关子树结构来保持平衡。

总之添加、删除的过程很复杂,要考虑的条件很多,具体实现就不细追究了,这里我们有个基本认识即可。正是这个复杂的保持平衡操作,使得平衡后的 B 树能够发挥出磁盘中快速查找的作用。

 1.6 应用场景

件系统和数据库系统中常用的B/B+ 树,他通过对每个节点存储个数的扩展,使得对连续的数据能够进行较快的定位和访问,能够有效减少查找时间,提高存储的空间局部性从而减少IO操作。他广泛用于文件系统及数据库中,如:

Windows:HPFS 文件系统

Mac:HFS,HFS+ 文件系统

Linux:ResiserFS,XFS,Ext3FS,JFS 文件系统

数据库:ORACLE,mysql,SQLSERVER 等中

数据库:ORACLE,MYSQL,SQLSERVER 等中

二 B+树

2.1 B+Tree树

B+Tree是在B-Tree基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现其索引结构。B+ 树,它比 B 树的查询性能更高。

一棵 B+ 树需要满足以下条件:

  1. 节点的子树数和关键字数相同(B 树是关键字数比子树数少一)
  2. 节点的关键字表示的是子树中的最大数,在子树中同样含有这个数据
  3. 叶子节点包含了全部数据,同时符合左小右大的顺序

 2.2 B+Tree与B-树的比较

1.不同点:

B-Tree结构图中可以看到每个节点中不仅包含数据的key值,还有data值。而每一个页的存储空间是有限的,如果data数据较大时将会导致每个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时同样会导致B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。

在B+Tree中,所有数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样可以大大加大每个节点存储的key值数量,降低B+Tree的高度。(可以理解为父辈点那一层只存储索引,可以存储更多的信息,父辈基数大,下一级的子节点存储数据就可以存储多个数据,多个数据快;高度变低了,上一辈基数大,下一辈孩子多

B+Tree相对于B-Tree有几点不同:

  1. 非叶子节点只存储键值信息。
  2. 所有叶子节点之间都有一个链指针。
  3. 数据记录都存放在叶子节点中。

 2.3 B+Tree的查找过程

假设每个磁盘块能存储4个键值及指针信息,则变成B+Tree后其结构如下图所示:

 1.通常在B+Tree上有两个头指针,一个指向根节点,另一个指向关键字最小的叶子节点而且所有叶子节点(即数据节点)之间是一种链式环结构。因此可以对B+Tree进行两种查找运算:一种是对于主键的范围查找和分页查找,另一种是从根节点开始,进行随机查找。

2.下面做一个推算:

InnoDB存储引擎中页的大小为16KB,一般表的主键类型为INT(占用4个字节)或BIGINT(占用8个字节),指针类型也一般为4或8个字节,也就是说一个页(B+Tree中的一个节点)中大概存储16KB/(8B+8B)=1K个键值(因为是估值,为方便计算,这里的K取值为〖10〗^3)。也就是说一个深度为3的B+Tree索引可以维护10^3 * 10^3 * 10^3 = 10亿 条记录。

3.实际情况中每个节点可能不能填充满,因此在数据库中,B+Tree的高度一般都在2~4层。MySQL的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只需要1~3次磁盘I/O操作。

 2.4 B+Tree的特点

1.关键字数和子树相同(B 树是关键字数比子树数少一)(节点8,15有两个子树,节点258有三个子树)

在 B 树中,节点的关键字用于在查询时确定查询区间,因此关键字数比子树数少一;而在 B+ 树中,节点的关键字代表子树的最大值,因此关键字数等于子树数。

2.非叶子节点仅用作索引,它的关键字和子节点有重复元素。所有的中间节点元素都同时存在于子节点,在子节点元素中是最大(或最小)元素。

有k个子树的中间节点包含有k个元素(B树中是k-1个元素),每个元素不保存数据,只用来索引,所有数据都保存在叶子节点。除叶子节点外的所有节点的关键字,都在它的下一级子树中同样存在,最后所有数据都存储在叶子节点中。

根节点的最大关键字其实就表示整个 B+ 树的最大元素。

3.叶子节点用指针连在一起

叶子节点包含了全部的数据,并且按顺序排列,B+ 树使用一个链表将它们排列起来,这样在查询时效率更快。

由于 B+ 树的中间节点不含有实际数据,只有子树的最大数据和子树指针因此磁盘页中可以容纳更多节点元素,也就是说同样数据情况下,B+ 树会 B 树更加“矮胖”,因此查询效率更快父一辈基数大,这一辈孩子就多,就能实现人口数据多,相比父辈基数少,这一辈孩子也会少,需要迭代好3,4代才能实现人口变多,增加迭代次数,相对于这里磁盘I/0的次数
4.B+ 树的查找必会查到叶子节点,更加稳定。有时候需要查询某个范围内的数据,由于 B+ 树的叶子节点是一个有序链表,只需在叶子节点上遍历即可,不用像 B 树那样挨个中序遍历比较大小。

概述总结:

  1. 层级更低,IO 次数更少
  2. 每次都需要查询到叶子节点,查询性能稳定
  3. 叶子节点形成有序链表,范围查询方便

 2.5 B+树自平衡过程

 https://blog.csdn.net/u011240877/article/details/8049066

 2.6  B+Tree的聚集索引与辅助索引

1.数据库中的B+Tree索引可以分为聚集索引(clustered index)和辅助索引(secondary index)。2.上面的B+Tree示例图在数据库中的实现即为聚集索引,聚集索引的B+Tree中的叶子节点存放的是整张表的行记录数据。

3.辅助索引与聚集索引的区别在于辅助索引的叶子节点并不包含行记录的全部数据,而是存储相应行数据的聚集索引键,即主键。

4.当通过辅助索引来查询数据时,InnoDB存储引擎会遍历辅助索引找到主键,然后再通过主键在聚集索引中找到完整的行记录数据。https://blog.csdn.net/sinat_31275315/article/details/107791765

三  B-,B+树的总结

3.1 磁盘读取的知识

系统从磁盘读取数据到内存时是以磁盘块(block)为基本单位的,位于同一个磁盘块中的数据会被一次性读取出来,而不是需要什么取什么。

InnoDB存储引擎中有页(Page)的概念,页是其磁盘管理的最小单位InnoDB存储引擎中默认每个页的大小为16KB,可通过参数innodb_page_size将页的大小设置为4K、8K、16K,在MySQL中可通过如下命令查看页的大小:

16384 除以 1024  =16kB

 而系统一个磁盘块的存储空间往往没有这么大,因此InnoDB每次申请磁盘空间时都会是若干地址连续磁盘块来达到页的大小16KB。InnoDB在把磁盘数据读入到内存时会以页为基本单位,在查询数据时,如果一个页中的每条数据都能有助于定位数据记录的位置,这将会减少磁盘I/O次数,提高查询效率。

3.2 B树,B+树的作用

b树,b+树的应用场景为:在磁盘上的海量数据中快速找到需要的数据,加载到内存中。

B-Tree是为磁盘等外存储设备设计的一种平衡查找树。B-Tree结构的数据可以让系统高效的找到数据所在的磁盘块。

与红黑树相比,在相同的的节点的情况下,一颗B/B+树的高度远远小于红黑树的高度(在下面B/B+树的性能分析中会提到)。B/B+树上操作的时间通常由存取磁盘的时间和CPU计算时间这两部分构成,而CPU的速度非常快,所以B树的操作效率取决于访问磁盘的次数,关键字总数相同的情况下B树的高度越小,磁盘I/O所花的时间越少。

3.3 为何使用b+树作为mysql的索引

1.数据库使用B+树肯定是为了提升查找效率。

2.B+树的磁盘读写代价更低。

由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,叶子节点包含了全部数据,并且按左小右大顺序排列,B+ 树使用一个链表将它们排列起来,这样在查询时效率更快。也就是说同样数据情况下,B+ 树会 B 树更加“矮胖”,因此查询效率更快。但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引。

3.B树在提高了IO性能的同时并没有解决元素遍历效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树不支持这样的操作或者说效率太低

4.相比B-树,B+树的父节点也必须存在于子节点中,是其中最大或者最小元素,B+树的节点只存储索引key值,具体信息的地址存在于叶子节点的地址中。这就使以页为单位的索引中可以存放更多的节点。减少更多的I/O支出。因此,B+树成为了数据库比较优秀的数据结构MySQL中MyIsAM和InnoDB都是采用的B+树结构。不同的是前者是非聚集索引,后者主键是聚集索引,所谓聚集索引是物理地址连续存放的索引,在取区间的时候,查找速度非常快,但同样的,插入的速度也会受到影响而降低。聚集索引的物理位置使用链表来进行存储。https://www.cnblogs.com/tiancai/p/9024351.htm

以上是关于一次聊个痛快,B-,B+树的特点和使用场景的主要内容,如果未能解决你的问题,请参考以下文章

数据结构复习:B树/B+树

转:基于B-树和B+树的使用:数据搜索和数据库索引的详细介绍

B-树和B+树的应用:数据搜索和数据库索引

B-树和B+树的应用

为什么MySQL数据库要用B+树存储索引

字符串练习题: 拓扑结构相同子树