B-tree(多路搜索树,并不是二叉的)是一种常见的数据结构。使用B-tree结构可以显著减少定位记录时所经历的中间过程,从而加快存取速度。B通常认为是Balance的简称。这个数据结构一般用于数据库的索引,综合效率较高。目前很多数据库产品的索引都是基于B+tree结构。MySQL也采用B+tree,它是B-tree的一个变种,其实特性基本上差不多,理解了B-tree也就懂了B+tree。

  • 一颗M阶B-Tree具有的特性

  1. 根结点的孩子数>=2(前提是树高度大于1)

  2. 除根结点与叶子结点,其他结点的孩子数为[ceil(m/2),m]个。ceil函数表示上取整数

  3. 所有叶子结点都出现在同一层,叶子结点不存储数据。

  4. 各个结点包含n个关键字信息:(P0,K1,P1,K2,P2......Kn,Pn)

      其中:

        4.1 Ki(i=1,2......n)为关键字,且K(i-1)

        4.2 关键字的个数n必须满足:[ceil(m/2)-1,m-1]

        4.3 Pi指向子树,且指针P(i-1)所指向的子树结点中所有关键字均小于Ki。即:父结点中任何关键字的左孩子都小于它,右孩子大于它。

  • B-Tree插入操作


  1. 插入新元素,如果叶子结点空间足够,则插入其中,遵循从小到大排序;

  2. 如果该结点空间满了,进行分裂。将该结点中一半关键字分裂到新结点中,中间关键字上移到父结点中。

  • 例子

将下面数字插入到一棵5阶B-Tree中:[3,14,7,1,8,5,11,17,13,6,23,12,20,26,4,16,18,24,25,19]

首先根据B-Tree特性知道,每个结点的关键字数量范围是:   2<=n<=4

【第一步】:插入3,14,7,1

到这里,第一个结点中关键字数量刚好满了。

【第二步】:插入8

由于8是大于7的,故应该插入右子树,一个结点中最多存储4个关键字,按照插入规则,将中间关键字7上移形成父结点,其他按照50%分裂成两个结点,如上图。

第三步】:插入5,11,17

由于5小于7,插入左子树,11,17大于7,插入右子树。叶子结点没有满4个关键字,故可以直接插入5,11,17

【第四步】:插入13

 

13大于7,应该插入右子树结点中,由于该结点中满4个关键字了,需要进行分裂。13刚好是中间关键字,上移到父结点中;其他按照50%分裂成两个结点。

【第五步】:插入6,23,12,20

 

以上几个数字按照规则直接插入即可,无需分裂操作。

【第六步】:插入26

 

由于26大于13,应该插入13的右子树结点中,但是该结点已经满了,需要分裂,将中间20上移到父结点中,其他按照50%分裂成两个结点。

【第七步】:插入4

 

由于4小于7,应该插入7的左结点中,但该结点满了,需要进行分裂,将中间关键字4上移到父结点中,其他按照50%分裂成两个结点。

【第八步】:插入16,18,24,25

以上4个数字按大小直接插入到相应位置即可,无需分裂操作。

【第九步】:插入19

 

插入19,需要放到18的后面,但是由于该结点已满,需要分裂操作,将中间关键字17上移到父结点中,其它按照50%分裂成14,16以及18,19两个结点;

别以为到这就结束了,再看17被上移到父结点中,由于父结点已经满了,所以这时对父结点进行分裂,将中间关键字13上移形成新的父结点,其他按照50%分裂成4,7和17,20两个结点,到此,数据插入全部完成,形成了一棵B-Tree。

  • 删除操作

  1. 查找B-tree中需删除的元素,如果该元素在B-tree中存在,则将该元素在其结点中进行删除。

  2. 删除该元素后,判断该元素是否有左右孩子结点,如果有,则上移孩子结点中的相近元素到父节点中(相近元素指的是:刚被删除元素的相邻后继元素,比如删除D,相近元素就是F等)

  3. 然后接着判断:如果结点中元素个数小于ceil(m/2)-1,首先找其相邻兄弟结点元素是否足够(结点中元素个数大于ceil(m/2)-1),如果足够,向父节点借一个元素,同时将借的元素的孩子结点中相邻后继元素上移到父结点中;如果其相邻兄弟都不够,即借完之后其结点元素个数小于ceil(m/2)-1,那进行合并,具体是:将父结点中元素下移到要合并结点中(该元素一般是位于两个合并结点的中间元素),然后进行合并。

  4. 以上操作按顺序进行递归执行

 

 总之,对于索引文件,无论是插入还是删除B-Tree结点,不断地分裂和合并结点来维持B-Tree结构是非常昂贵的操作。


  • B+tree介绍

MySQL索引采用B+Tree,它是应文件系统所需而产生的一种B-tree的变形树,他们的差异在于:

   1) 非叶子结点的子树指针与关键字个数相同;

2) B+树父结点中的记录,存储的是下层子树中的最小值;
3) 所有叶子结点通过一个链指针相连;
4) 所有关键字都在叶子结点出现;

如,下面是一棵典型的B+tree(假设每个结点最多有4个关键字)  

其他特性与操作与B-tree基本相同。到此,B-tree和B+tree基础知识已经了解了,下面的内容都是基于以上的概念。