咱们都知道,innodb中的索引结构使用的是B+树。B+树是一种B树的变形树,而B树又是来源于平衡二叉树。相较于平衡二叉树,B树更适合磁盘场景下文件索引系统。那为何B树更适合磁盘场景,B+树又在B树基础上作了什么优化?抱着这些问题,本博客将深刻分析B树和B+树前因后果,其中会涉及到二叉排序树和平衡二叉树等数据结构。数据结构
二叉排序树、平衡二叉树、B树和B+树都是基于二分法的思路来优化查找的。性能
要谈B树,首先要理解平衡二叉树。而平衡二叉树又是从二叉排序树优化而来。让咱们追根溯源先看看二叉排序树。这个数据结构很是容易理解,知足以下性质即为一棵二叉排序树优化
中序遍历一棵二叉排序树能获得一个递增的有序序列指针
问题:二叉排序树的查找性能受限于它的结构,若是结构合理,复杂度能达到二分法的复杂度,O(log2N);而在极端状况下,往二叉排序树中插入结点,会获得一个单链表,这样时间复杂度就是O(N)了。树的高度越小,查找速度越快。blog
为了使二叉排序树高度尽量小,平衡二叉树诞生了,也成为AVL树。这种数据结构,是具有以下特征的二叉排序树:排序
平衡二叉树经过“旋转”操做来保证左右子树深度之差小于等于1,从而保证树的高度尽量小。索引
问题:虽然说平衡二叉树能够保证必定的查找效率。即使这样,平衡二叉树方法也只适用于存储在内存中的较小的文件,若是是查找存放在外存中很大的文件,每访问一个结点就须要进行磁盘IO,因为节点众多,开销依然很大。内存
既然结点太多了,那将多个结点合成一个节点如何,是否能够减小IO次数。这样也至关于将二叉树变成多叉树。B树也是基于这个思路来实现的,即一个结点包含多个关键字(表示要查找的目标数值)和多个指向子树的指针。
对于一个m阶的B树,拥有以下特性:博客
在实际的文件系统场景中,B树的结点规模通常以一个磁盘页为单位,因此m值取决于磁盘页大小innodb
结点结构以下所示
第一个元素n记录当前结点的关键字数量。K1 - Kn 表示各个关键字,P0 - Pn 表示指向子树根结点的指针。重要性质:Pi所指向子树中全部结点关键字均大于Ki,而且小于Ki+1。B树即是依据此性质来完成查找。
所以,B树查找的过程与平衡二叉树相似,无需赘述。可是插入和删除不太同样。
以插入为例,假设要向一个m阶B树插入某个关键字,B树会先查找该关键字在最底层的位置,向对应的位置插入。同时保证结点数量不超过m-1,若是超过数量限制,则会进行结点“分裂”,以中间关键字为界将结点一分为二,并将中间关键字向上插入到双亲结点上,若双亲结点已满,用一样的方法继续分解,直到分解到根节点,此时B树高度加1。
对于删除而言,同理,若是结点的关键字删除后数量小于(m/2)-1则要进行“合并”结点的操做。除了删除关键字,还须要删除关键字邻近的指针。例如删除Ki时,也须要删除Pi。这里就有两种状况,若删除的结点Pi是最低层,直接删除Ki和Pi便可,由于指针指向null;若是不是,不能直接删,由于Pi指向一棵子树,这时须要将Pi指向的子树里最小的关键字与待删除关键字互换,互换以后目标关键字必定是转移到了最低一层,将其和相邻指针删除便可。
B树已是很优秀的数据结构了,为何还须要B+树呢?先来看看两者的区别。B+树是B树的变形树,它们的差别以下:
B+树的随机查找、插入和删除过程基本与B树相似
1.查询的IO次数更少
其实上文在B树中提到的关键字,不只仅只是数值,也包含具体的数据。读取结点,也会把各个关键字对应的具体数据读取出来。而在B+树中对于非终端节点而言,每个关键字只是一个索引,不包含其它数据(只有终端结点才会引用具体数据)。所以,在单个结点大小一致的前提下(取磁盘页的大小),B+树中每一个非终端结点结点能够存储更多元素,所以树的分支会更多,更加矮胖,查询的IO次数也更少。
2.查询性能稳定
B树查询不一样结点性能可能不同,由于目标元素可能位于根节点,也可能在叶子结点上。可是B+树中有效结点必定在最低层,因此每次查找必须查到叶子结点,性能稳定。
3.便于范围查询
范围查询,B树须要进行屡次二分查找。而B+树只须要一次二分查找找到下限,以后再顺着链表找到上限便可。叶子结点经过链表互相链接这一事实,决定了B+树在范围查询上的优点。
综上所述,在MySQL索引结构中使用B+树性能要更优于B树。