要回答好这个问题,首先咱们要弄懂什么是索引?索引常见的数据结构有哪些?这些数据结构有何优缺点?只有弄懂这些,再去比较,才会知道为啥要用B+树做为MySQL数据库的存储索引了。html
MySQL官方对索引的定义为:索引(Index)是帮助MySQL高效获取数据的数据结构。它的本质就是数据结构,单独存储在磁盘上,用它来提升数据查询的效率。android
适合做为索引的结构应该是尽量少的执行磁盘IO操做,由于执行磁盘IO操做很是的耗时。面试
采起二分查找的思想,O(log N)的复杂度就能够完成对数据的查找任务,查找所需的最大次数等同于二叉查找树的高度。算法
它具备如下特性:数据库
以下图所示:排序工具数据结构
对该二叉树的节点进行查找发现深度为1的节点的查找次数为1,深度为2的查找次数为2,深度为n的节点的查找次数为n,所以其平均查找次数为 (1+2+2+3+3+3) / 6 = 2.3次app
二叉查找树能够任意地构造,这样会出现一种极端状况,若是依次插入以下六个节点:7,6,5,4,那么就会变成下图所示:工具
这样退化成线性表,致使树高度太高,从而查询效率就下降了。性能
那么如何解决二叉查找树屡次插入新节点而致使的不平衡?这里就要引出新的定义——平衡二叉树,或称AVL树。优化
树的查找性能取决于树的高度,让树尽量平衡,就是为了下降树的高度。
平衡二叉查找树(AVL树)在符合二叉查找树的条件下,还知足任何节点的两个子树的高度最大差为1。以下图所示,它的任何节点的两个子树的高度差<=1。
若是在AVL树中进行插入或删除节点,可能致使AVL树失去平衡。
红黑树是一种自平衡的二叉查找树。除了符合二叉查找树的基本特性外,它还具有如下特性:
节点是红色或者黑色;
每一个叶子的节点都是黑色的空节点(NULL);
每一个红色节点的两个子节点都是黑色的;
从任意节点到其每一个叶子的全部路径都包含相同的黑色节点。
红黑树相比于BST和AVL树有什么优势?
红黑树是牺牲了严格的高度平衡的优越条件为代价,它只要求部分地达到平衡要求,下降了对旋转的要求,从而提升了性能。
红黑树可以以O(log2 n)的时间复杂度进行搜索、插入、删除操做。此外,因为它的设计,任何不平衡都会在三次旋转以内解决。固然,还有一些更好的,但实现起来更复杂的数据结构可以作到一步旋转以内达到平衡,但红黑树可以给咱们一个比较“便宜”的解决方案。
相比于BST,由于红黑树能够能确保树的最长路径不大于两倍的最短路径的长度,因此能够看出它的查找效果是有最低保证的。在最坏的状况下也能够保证O(logN)的,这是要好于二叉查找树的。由于二叉查找树最坏状况可让查找达到O(N)。
红黑树的算法时间复杂度和AVL相同,但统计性能比AVL树更高,因此在插入和删除中所作的后期维护操做确定会比红黑树要耗时好多,可是他们的查找效率都是O(logN),因此红黑树应用仍是高于AVL树的。实际上插入,AVL 树和红黑树的速度取决于你所插入的数据.若是你的数据分布较好,则比较宜于采用 AVL树(例如随机产生系列数),可是若是你想处理比较杂乱的状况,则红黑树是比较快的。
- AVL是严格平衡树,所以在增长或者删除节点的时候,根据不一样状况,旋转的次数比红黑树要多;
- 而红黑是弱平衡的,用非严格的平衡来换取增删节点时候旋转次数的下降;
- 因此简单说,查找的次数远远大于插入和删除,那么选择AVL树;若是搜索、插入删除次数几乎差很少,应该选择RB树。
红黑树的应用
红黑树的各类操做的时间复杂度是O(lgn),逻辑上很近的节点(父子)物理上可能很远,没法利用局部性,IO次数多查找慢,效率低。
红黑树的高度虽然有必定的控制,而数据库当中通常要把索引树的高度控制在3-5层,这点红黑树显然没法作到。B-Tree是为磁盘等外存储设备设计的一种平衡查找树,是一种多路平衡搜索树,既然它是多路平衡的,那么就不在像红黑树那样只有2个子节点了,既然有多个子节点,树的高度就能够控制了,同时它也跟红黑树同样,数据是排序的,能够快速查找。
B树具备如下特色:
下面是一颗B树:
B-Tree结构的数据可让系统高效的找到数据所在的磁盘块。为了描述B-Tree,首先定义一条记录为一个二元组[key, data] ,key为记录的键值,对应表中的主键值,data为一行记录中除主键外的数据。对于不一样的记录,key值互不相同。
B-Tree中的每一个节点根据实际状况能够包含大量的关键字信息和分支,以下图所示为一个3阶的B-Tree:
每一个节点占用一个盘块的磁盘空间,一个节点上有两个升序排序的关键字和三个指向子树根节点的指针,指针存储的是子节点所在磁盘块的地址。两个关键词划分红的三个范围域对应三个指针指向的子树的数据的范围域。以根节点为例,关键字为17和35,P1指针指向的子树的数据范围为小于17,P2指针指向的子树的数据范围为17~35,P3指针指向的子树的数据范围为大于35。
模拟查找关键字29的过程:
分析上面过程,发现须要3次磁盘I/O操做,和3次内存查找操做。因为内存中的关键字是一个有序表结构,能够利用二分法查找提升效率。而3次磁盘I/O操做是影响整个B-Tree查找效率的决定因素。
B+Tree是在B-Tree(不要读成B减树,而是B树)基础上的一种优化,使其更适合实现外存储索引结构,InnoDB存储引擎就是用B+Tree实现其索引结构。
从上一节中的B-Tree结构图中能够看到每一个节点中不只包含数据的key值,还有data值。而每个页的存储空间是有限的,若是data数据较大时将会致使每一个节点(即一个页)能存储的key的数量很小,当存储的数据量很大时一样会致使B-Tree的深度较大,增大查询时的磁盘I/O次数,进而影响查询效率。在B+Tree中,全部数据记录节点都是按照键值大小顺序存放在同一层的叶子节点上,而非叶子节点上只存储key值信息,这样能够大大加大每一个节点存储的key值数量,下降B+Tree的高度。
B+Tree相对于B-Tree有几点不一样:
将上一节中的B-Tree优化,因为B+Tree的非叶子节点只存储键值信息,假设每一个磁盘块能存储4个键值及指针信息,则变成B+Tree后其结构以下图所示:
数据都在叶子节点上,而且增长了顺序访问指针,每一个叶子节点都指向相邻的叶子节点的地址。相比B-Tree来讲,进行范围查找时只须要查找两个节点,进行遍历便可,提升了区间访问性能(无需返回上层父节点重复遍历查找减小IO操做)。而B-Tree须要获取全部节点,相比之下B+Tree效率更高。
红黑树等数据结构也能够用来实现索引,可是文件系统及数据库系统广泛采用B-/+Tree做为索引结构。
通常来讲,索引自己也很大,不可能所有存储在内存中,所以索引每每以索引文件的形式存储的磁盘上。这样的话,索引查找过程当中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,因此评价一个数据结构做为索引的优劣最重要的指标就是在查找过程当中磁盘I/O操做次数的渐进复杂度。换句话说,索引的结构组织要尽可能减小查找过程当中磁盘I/O的存取次数。
这样咱们对比上面的B+树和红黑树,好比查找节点21,红黑树要磁盘IO5次,而B+树只要2次,也就是说磁盘IO次数大体为树的高度,这样B+树就脱颖而出了,成为实现索引的不二选择。
实际状况中每一个节点可能不能填充满,所以在数据库中,B+Tree的高度通常都在2~4层。MySQL的InnoDB存储引擎在设计时是将根节点常驻内存的,也就是说查找某一键值的行记录时最多只须要1~3次磁盘I/O操做。
数据库中的B+Tree索引能够分为汇集索引(clustered index)和辅助索引(secondary index)。上面的B+Tree示例图在数据库中的实现即为汇集索引,汇集索引的B+Tree中的叶子节点存放的是整张表的行记录数据。辅助索引与汇集索引的区别在于辅助索引的叶子节点并不包含行记录的所有数据,而是存储相应行数据的汇集索引键,即主键。当经过辅助索引来查询数据时,InnoDB存储引擎会遍历辅助索引找到主键,而后再经过主键在汇集索引中找到完整的行记录数据。
聚簇索引(汇集索引):并非一种单独的索引类型,而是一种数据存储方式。具体细节取决于不一样的实现,InnoDB的聚簇索引其实就是在同一个结构中保存了B-Tree索引(技术上来讲是B+Tree)和数据行。
数据库索引采用B+树而不是B树的主要缘由:B+树只要遍历叶子节点就能够实现整棵树的遍历,并且在数据库中基于范围的查询是很是频繁的,而B树只能中序遍历全部节点,效率过低。
文件与数据库都是须要较大的存储,也就是说,它们都不可能所有存储在内存中,故须要存储到磁盘上。而所谓索引,则为了数据的快速定位与查找,那么索引的结构组织要尽可能减小查找过程当中磁盘I/O的存取次数,所以B+树相比B树更为合适。数据库系统巧妙利用了局部性原理与磁盘预读原理,将一个节点的大小设为等于一个页,这样每一个节点只须要一次I/O就能够彻底载入,而红黑树这种结构,高度明显要深的多,而且因为逻辑上很近的节点(父子)物理上可能很远,没法利用局部性。最重要的是,B+树还有一个最大的好处:方便扫库。B树必须用中序遍历的方法按序扫库,而B+树直接从叶子结点挨个扫一遍就完了,B+树支持range-query很是方便,而B树不支持,这是数据库选用B+树的最主要缘由。
问:为何索引结构默认使用B-Tree,而不是hash,二叉树,红黑树?
hash:虽然能够快速定位,可是没有顺序,IO复杂度高。
二叉树:树的高度不均匀,不能自平衡,查找效率跟数据有关(树的高度),而且IO代价高。
红黑树:树的高度随着数据量增长而增长,IO代价高。
若是只选一个数据,那确实是hash更快。可是数据库中常常会选择多条,这时候因为B+树索引有序,而且又有链表相连,它的查询效率比hash就快不少了。
并且数据库中的索引通常是在磁盘上,数据量大的状况可能没法一次装入内存,B+树的设计能够容许数据分批加载,同时树的高度较低,提升查找效率。
问:为何官方建议使用自增加主键做为索引。
结合B+Tree的特色,自增主键是连续的,在插入过程当中尽可能减小页分裂,即便要进行页分裂,也只会分裂不多一部分。而且能减小数据的移动,每次插入都是插入到最后。总之就是减小分裂和移动的频率。
参考:
https://blog.csdn.net/majiawenzzz/article/details/81098870
https://blog.csdn.net/UFO___/article/details/80522453
https://www.cnblogs.com/liqiangchn/p/9060521.html
https://www.jianshu.com/p/486a514b0ded
https://blog.csdn.net/ifollowrivers/article/details/73614549