【数据结构】B树、B+树详解

B树

前言  mysql

  首先,为何要总结B树、B+树的知识呢?最近在学习数据库索引调优相关知识,数据库系统广泛采用B-/+Tree做为索引结构(例如mysql的InnoDB引擎使用的B+树),理解不透彻B树,则没法理解数据库的索引机制;接下来将用最简洁直白的内容来了解B树、B+树的数据结构算法

  另外,B-树,即为B树。由于B树的原英文名称为B-tree,而国内不少人喜欢把B-tree译做B-树,其实,这是个很是很差的直译,很容易让人产生误解。如人们可能会觉得B-树是一种树,而B树又是一种树。而事实上是,B-tree就是指的B树,目前理解B的意思为平衡sql

  B树的出现是为了弥合不一样的存储级别之间的访问速度上的巨大差别,实现高效的 I/O。平衡二叉树的查找效率是很是高的,并能够经过下降树的深度来提升查找的效率。可是当数据量很是大,树的存储的元素数量是有限的,这样会致使二叉查找树结构因为树的深度过大而形成磁盘I/O读写过于频繁,进而致使查询效率低下。另外数据量过大会致使内存空间不够容纳平衡二叉树全部结点的状况。B树是解决这个问题的很好的结构数据库

 

概念数据结构

  首先,B树不要和二叉树混淆,在计算机科学中B树是一种自平衡树数据结构,它维护有序数据并容许以对数时间进行搜索,顺序访问,插入和删除。B树是二叉搜索树的通常化,由于节点能够有两个以上的子节点。[1]与其余自平衡二进制搜索树不一样,B树很是适合读取和写入相对较大的数据块(如光盘)的存储系统。它一般用于数据库文件系统性能

 

定义学习

B树是一种平衡的多分树,一般咱们说m阶的B树,它必须知足以下条件: 大数据

  • 每一个节点最多只有m个子节点。
  • 每一个非叶子节点(除了根)具备至少⌈ m/2⌉子节点。
  • 若是根不是叶节点,则根至少有两个子节点。
  • 具备k个子节点的非叶节点包含k -1个键。
  • 全部叶子都出如今同一水平,没有任何信息(高度一致)。

第一次看到这个定义的时候,在想什么鬼?。。。。什么是阶?子节点、飞叶子点、根???啥意思!少年别慌。。。优化

什么是B树的阶 ?spa

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

全部节点中,节点【13,16,19】拥有的子节点数目最多,四个子节点(灰色节点),因此能够定义上面的图片为4阶B树,如今懂什么是阶了吧

什么是根节点 ?

节点【10】即为根节点,特征:根节点拥有的子节点数量的上限和内部节点相同,若是根节点不是树中惟一节点的话,至少有俩个子节点(否则就变成单支了)。在m阶B树中(根节点非树中惟一节点),那么有关系式2<= M <=m,M为子节点数量;包含的元素数量 1<= K <=m-1,K为元素数量。

什么是内部节点 ?

节点【13,16,19】、节点【3,6】都为内部节点,特征:内部节点是除叶子节点和根节点以外的全部节点,拥有父节点和子节点。假定m阶B树的内部节点的子节点数量为M,则必定要符合(m/2)<=  M <=m关系式,包含元素数量M-1;包含的元素数量 (m/2)-1<= K <=m-1,K为元素数量。m/2向上取整。

什么是叶子节点?

节点【1,2】、节点【11,12】等最后一层都为叶子节点,叶子节点对元素的数量有相同的限制,可是没有子节点,也没有指向子节点的指针。特征:在m阶B树中叶子节点的元素符合(m/2)-1<= K <=m-1。

好了,概念已经清楚,不用着急背公式, 接着往下看

 

插入

针对m阶高度h的B树,插入一个元素时,首先在B树中是否存在,若是不存在,即在叶子结点处结束,而后在叶子结点中插入该新的元素。

  • 若该节点元素个数小于m-1,直接插入;
  • 若该节点元素个数等于m-1,引发节点分裂;以该节点中间元素为分界,取中间元素(偶数个数,中间两个随机选取)插入到父节点中;
  • 重复上面动做,直到全部节点符合B树的规则;最坏的状况一直分裂到根节点,生成新的根节点,高度增长1;

上面三段话为插入动做的核心,接下来以5阶B树为例,详细讲解插入的动做;

5阶B树关键点:

  • 2<=根节点子节点个数<=5
  • 3<=内节点子节点个数<=5
  • 1<=根节点元素个数<=4
  • 2<=非根节点元素个数<=4

     插入8          

图(1)插入元素【8】后变为图(2),此时根节点元素个数为5,不符合 1<=根节点元素个数<=4,进行分裂(真实状况是先分裂,而后插入元素,这里是为了直观而先插入元素,下面的操做都同样,再也不赘述),取节点中间元素【7】,加入到父节点,左右分裂为2个节点,如图(3)

接着插入元素【5】,【11】,【17】时,不须要任何分裂操做,如图(4)

   

插入元素【13】

节点元素超出最大数量,进行分裂,提取中间元素【13】,插入到父节点当中,如图(6)

    

 

 接着插入元素【6】,【12】,【20】,【23】时,不须要任何分裂操做,如图(7)

插入【26】时,最右的叶子结点空间满了,须要进行分裂操做,中间元素【20】上移到父节点中,注意经过上移中间元素,树最终仍是保持平衡,分裂结果的结点存在2个关键字元素。

 

插入【4】时,致使最左边的叶子结点被分裂,【4】刚好也是中间元素,上移到父节点中,而后元素【16】,【18】,【24】,【25】陆续插入不须要任何分裂操做

 

最后,当插入【19】时,含有【14】,【16】,【17】,【18】的结点须要分裂,把中间元素【17】上移到父节点中,可是状况来了,父节点中空间已经满了,因此也要进行分裂,将父节点中的中间元素【13】上移到新造成的根结点中,这样具体插入操做的完成。

 

删除

首先查找B树中需删除的元素,若是该元素在B树中存在,则将该元素在其结点中进行删除;删除该元素后,首先判断该元素是否有左右孩子结点,若是有,则上移孩子结点中的某相近元素(“左孩子最右边的节点”或“右孩子最左边的节点”)到父节点中,而后是移动以后的状况;若是没有,直接删除。

  • 某结点中元素数目小于(m/2)-1,(m/2)向上取整,则须要看其某相邻兄弟结点是否丰满;
  • 若是丰满(结点中元素个数大于(m/2)-1),则向父节点借一个元素来知足条件;
  • 若是其相邻兄弟都不丰满,即其结点数目等于(m/2)-1,则该结点与其相邻的某一兄弟结点进行“合并”成一个结点;

接下来还以5阶B树为例,详细讲解删除的动做;

  • 关键要领,元素个数小于 2(m/2 -1)就合并,大于4(m-1)就分裂

如图依次删除依次删除【8】,【20】,【18】,【5】

首先删除元素【8】,固然首先查找【8】,【8】在一个叶子结点中,删除后该叶子结点元素个数为2,符合B树规则,操做很简单,我们只须要移动【11】至原来【8】的位置,移动【12】至【11】的位置(也就是结点中删除元素后面的元素向前移动)

 

 下一步,删除【20】,由于【20】没有在叶子结点中,而是在中间结点中找到,我们发现他的继承者【23】(字母升序的下个元素),将【23】上移到【20】的位置,而后将孩子结点中的【23】进行删除,这里刚好删除后,该孩子结点中元素个数大于2,无需进行合并操做。

下一步删除【18】,【18】在叶子结点中,可是该结点中元素数目为2,删除致使只有1个元素,已经小于最小元素数目2,而由前面咱们已经知道:若是其某个相邻兄弟结点中比较丰满(元素个数大于ceil(5/2)-1=2),则能够向父结点借一个元素,而后将最丰满的相邻兄弟结点中上移最后或最前一个元素到父节点中,在这个实例中,右相邻兄弟结点中比较丰满(3个元素大于2),因此先向父节点借一个元素【23】下移到该叶子结点中,代替原来【19】的位置,【19】前移;然【24】在相邻右兄弟结点中上移到父结点中,最后在相邻右兄弟结点中删除【24】,后面元素前移。

最后一步删除【5】, 删除后会致使不少问题,由于【5】所在的结点数目恰好达标,恰好知足最小元素个数(ceil(5/2)-1=2),而相邻的兄弟结点也是一样的状况,删除一个元素都不能知足条件,因此须要该节点与某相邻兄弟结点进行合并操做;首先移动父结点中的元素(该元素在两个须要合并的两个结点元素之间)下移到其子结点中,而后将这两个结点进行合并成一个结点。因此在该实例中,我们首先将父节点中的元素【4】下移到已经删除【5】而只有【6】的结点中,而后将含有【4】和【6】的结点和含有【1】,【3】的相邻兄弟结点进行合并成一个结点。

 

也许你认为这样删除操做已经结束了,其实否则,在看看上图,对于这种特殊状况,你当即会发现父节点只包含一个元素【7】,没达标(由于非根节点包括叶子结点的元素K必须知足于2=<K<=4,而此处的K=1),这是不可以接受的。若是这个问题结点的相邻兄弟比较丰满,则能够向父结点借一个元素。而此时兄弟节点元素恰好为2,刚刚知足,只能进行合并,而根结点中的惟一元素【13】下移到子结点,这样,树的高度减小一层。

看完插入,删除,想必也把B树的特征掌握了,下面普及下其余知识,换个脑子

 

 

磁盘IO与预读

  计算机存储设备通常分为两种:内存储器(main memory)和外存储器(external memory)。 

  内存储器为内存,内存存取速度快,但容量小,价格昂贵,并且不能长期保存数据(在不通电状况下数据会消失)。

  外存储器即为磁盘读取,磁盘读取数据靠的是机械运动,每次读取数据花费的时间能够分为寻道时间、旋转延迟、传输时间三个部分,寻道时间指的是磁臂移动到指定磁道所须要的时间,主流磁盘通常在5ms如下;旋转延迟就是咱们常常据说的磁盘转速,好比一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2 = 4.17ms;传输时间指的是从磁盘读出或将数据写入磁盘的时间,通常在零点几毫秒,相对于前两个时间能够忽略不计。那么访问一次磁盘的时间,即一次磁盘IO的时间约等于5+4.17 = 9ms左右,听起来还挺不错的,但要知道一台500 -MIPS的机器每秒能够执行5亿条指令,由于指令依靠的是电的性质,换句话说执行一次IO的时间能够执行40万条指令,数据库动辄十万百万乃至千万级数据,每次9毫秒的时间,显然是个灾难。下图是计算机硬件延迟的对比图,供你们参考:

   考虑到磁盘IO是很是高昂的操做,计算机操做系统作了一些优化,当一次IO时,不光把当前磁盘地址的数据,而是把相邻的数据也都读取到内存缓冲区内,由于局部预读性原理告诉咱们,当计算机访问一个地址的数据的时候,与其相邻的数据也会很快被访问到。每一次IO读取的数据咱们称之为一页(page)。具体一页有多大数据跟操做系统有关,通常为4k或8k,也就是咱们读取一页内的数据时候,实际上才发生了一次IO,这个理论对于索引的数据结构设计很是有帮助。

事实1 : 不一样容量的存储器,访问速度差别悬殊。

  • 磁盘(ms级别) << 内存(ns级别), 100000倍
  • 若内存访问须要1s,则一次外存访问须要一天
  • 为了不1次外存访问,宁愿访问内存100次...因此将最经常使用的数据存储在最快的存储器中

事实2 : 从磁盘中读 1 B,与读写 1KB 的时间成本几乎同样

从以上数据中能够总结出一个道理,索引查询的数据主要受限于硬盘的I/O速度,查询I/O次数越少,速度越快,因此B树的结构才应需求而生;B树的每一个节点的元素能够视为一次I/O读取,树的高度表示最多的I/O次数,在相同数量的总元素个数下,每一个节点的元素个数越多,高度越低,查询所需的I/O次数越少;假设,一次硬盘一次I/O数据为8K,索引用int(4字节)类型数据创建,理论上一个节点最多能够为2000个元素,2000*2000*2000=8000000000,80亿条的数据只需3次I/O(理论值),可想而知,B树作为索引的查询效率有多高;

另外也能够看出一样的总元素个数,查询效率和树的高度密切相关

 

B树的高度

一棵含有N个总关键字数的m阶的B树的最大高度是多少?

  log(m/2)(N+1)/2 + 1  ,log以(m/2)为低,(N+1)/2的对数再加1

算法以下

 

 

 

B+树

   B+树是应文件系统所需而产生的B树的变形树,那么可能必定会想到,既然有了B树,又出一个B+树,那B+树必然是有不少优势的

 

B+树的特征:

  • 有m个子树的中间节点包含有m个元素(B树中是k-1个元素),每一个元素不保存数据,只用来索引;
  • 全部的叶子结点中包含了所有关键字的信息,及指向含有这些关键字记录的指针,且叶子结点自己依关键字的大小自小而大的顺序连接。 (而B 树的叶子节点并无包括所有须要查找的信息);
  • 全部的非终端结点能够当作是索引部分,结点中仅含有其子树根结点中最大(或最小)关键字。 (而B 树的非终节点也包含须要查找的有效信息);

 

为何说B+树比B树更适合数据库索引?

1)B+树的磁盘读写代价更低

  B+树的内部结点并无指向关键字具体信息的指针。所以其内部结点相对B 树更小。若是把全部同一内部结点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多。一次性读入内存中的须要查找的关键字也就越多。相对来讲IO读写次数也就下降了;

2)B+树查询效率更加稳定

  因为非终结点并非最终指向文件内容的结点,而只是叶子结点中关键字的索引。因此任何关键字的查找必须走一条从根结点到叶子结点的路。全部关键字查询的路径长度相同,致使每个数据的查询效率至关;

3)B+树便于范围查询(最重要的缘由,范围查找是数据库的常态)

  B树在提升了IO性能的同时并无解决元素遍历的我效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只须要去遍历叶子节点就能够实现整棵树的遍历。并且在数据库中基于范围的查询是很是频繁的,而B树不支持这样的操做或者说效率过低;不懂能够看看这篇解读-》范围查找

 

补充:B树的范围查找用的是中序遍历,而B+树用的是在链表上遍历;

B+树以下:

相关文章
相关标签/搜索