mysql数据库默认存储引擎InnoDB,是以B+Tree的结构存储数据;而要说清楚缘由,就得先介绍一下Tree这种结构
首先是最简单的二叉树
我们知道,向二叉树中插入或查找数据时,是以比较节点数据大小来决定存放在哪个子节点的。如下图
二叉树具有如下特点:
1.左子树节点必小于上一级节点,右子树节点比大于上一级节点
2.没有相等的节点
则上图从小到大的顺序 2---3---5----6----7----8
一般情况下,二叉树通过节点的比较减下了查询范围,要比线性查找快
但是如果,二叉树中某个分叉特别长呢,极端情况下,就会形成一个链表,即变成了线性查找
为解决二叉树的不稳定性,引入了AVL树(严格的平衡二叉树),用平衡因子差值判断是否平衡,通过旋转实现平衡,这样保证了各个分叉高度的一致性,一般来说,平衡树的树高最低,查询效率最好。
但是,有利就有弊,旋转调整是非常耗时的,如果数据需要经常的修改、删除、增加,则效率大大降低,适用于插入数据后,很少修改的场景。
应用
1、Windows NT内核中广泛存在;
红黑树
说了以上两种情况,就引出了红黑树,其实就是作为以上两种结果的折中处理,红黑树通过对树节点加上着色(每个节点增加一个存储位red/black,即红黑树的由来)标记判断路径长度,确保没有任何一条路径超出其他路径的两倍,是一中弱平衡数,相对于AVL,在插入修改时,旋转更少,相对于二叉树,避免了多大的不平衡性,造成的效率不稳定。
特点:
1.每个节点非黑即红
2.根节点必须是黑
3.叶子节点(树尾端节点或null指针)必须是黑
4.如果一个节点是红,那它的子节点必须是黑的
应用:
1.在Java8中 HashMap的底层实现(由数组+链表--》数组+红黑树)
2.TreeMap
3.Nginx用用红黑树管理timer
说了这么多,总算说到了B+Tree
其实你一定还听过B-Tree(不读B减数,-代表占位符而已,就读B树)
我们在MySQL中的数据一般是放在磁盘中的,读取数据的时候肯定会有访问磁盘的操作,磁盘中有两个机械运动的部分,分别是盘片旋转和磁臂移动。盘片旋转就是我们市面上所提到的多少转每分钟,而磁盘移动则是在盘片旋转到指定位置以后,移动磁臂后开始进行数据的读写。那么这就存在一个定位到磁盘中的块的过程,而定位是磁盘的存取中花费时间比较大的一块,毕竟机械运动花费的时候要远远大于电子运动的时间。当大规模数据存储到磁盘中的时候,显然定位是一个非常花费时间的过程,但是我们可以通过B树进行优化,提高磁盘读取时定位的效率。
为什么B类树可以进行优化呢?我们可以根据B类树的特点,构造一个多阶的B类树,然后在尽量多的在结点上存储相关的信息,保证层数尽量的少,以便后面我们可以更快的找到信息,磁盘的I/O操作也少一些,而且B类树是平衡树,每个结点到叶子结点的高度都是相同,这也保证了每个查询是稳定的。
总的来说,B/B+树是为了磁盘或其它存储设备而设计的一种平衡多路查找树(相对于二叉,B树每个内节点有多个分支),与红黑树相比,在相同的的节点的情况下,一颗B/B+树的高度远远小于红黑树的高度(在下面B/B+树的性能分析中会提到)。
B/B+树上操作的时间通常由存取磁盘的时间和CPU计算时间这两部分构成,而CPU的速度非常快,所以B树的操作效率取决于访问磁盘的次数,关键字总数相同的情况下B树的高度越小,磁盘I/O所花的时间越少。
B-Tree内部结构
太多的细节就不说,但是强调一点,
这里只是一个简单的B树,在实际中B树节点中关键字很多的,上面的图中比如35节点,35代表一个key(索引),而小黑块代表的是这个key所指向的内容在内存中实际的存储位置,是一个指针。
而B+Tree是一种B-Tree的变形,只有树的最尾部的叶子节点保存数据,其他节点都只保存索引(这类似于文件系统目录,一级一级查询到最后的数据)举个文件查找的例子:有3个文件夹a、b、c, a包含b,b包含c,一个文件d,a、b、c就是索引(存储在非叶子节点), a、b、c只是要找到的d的key,而实际的数据d存储在叶子节点上
、为什么说B+树比B树更适合数据库索引?
1、 B+树的磁盘读写代价更低:B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更小,如果把所有同一内部节点的关键字存放在同一盘块中,那么盘块所能容纳的关键字数量也越多,一次性读入内存的需要查找的关键字也就越多,相对IO读写次数就降低了。
2、B+树的查询效率更加稳定:由于非终结点并不是最终指向文件内容的结点,而只是叶子结点中关键字的索引。所以任何关键字的查找必须走一条从根结点到叶子结点的路。所有关键字查询的路径长度相同,导致每一个数据的查询效率相当。
3、由于B+树的数据都存储在叶子结点中,分支结点均为索引,方便扫库,只需要扫一遍叶子结点即可,但是B树因为其分支结点同样存储着数据,我们要找到具体的数据,需要进行一次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树用于数据库索引。
PS:我在知乎上看到有人是这样说的,我感觉说的也挺有道理的:
他们认为数据库索引采用B+树的主要原因是:B树在提高了IO性能的时候并没有解决元素遍历时效率低下的问题,正是为了解决这个问题,B+树应用而生。B+树只需要去遍历叶子节点就可以实现整棵树的遍历。而且在数据库中基于范围的查询是非常频繁的,而B树效率太低。
小结:
二叉树:每个结点只存储一个关键字,等于则命中,小于走左结点,大于走右结点;
B-树:多路平衡搜索树,每个结点存储M/2到M个关键字,非叶子结点存储指向关键字范围的子结点;所有关键字在整颗树中出现,且只出现一次,非叶子结点可以命中;
B+树:在B-树基础上,为叶子结点增加链表指针,所有关键字都在叶子结点中出现,非叶子结点作为叶子结点的索引;B+树总是到叶子结点才命中;
B*树:在B+树基础上,为非叶子结点也增加链表指针,将结点的最低利用率从1/2提高到2/3;