为了讲清楚这个问题,阿粉先带你们了解一下什么是索引。sql
我记得刚刚学习数据库的时候,老师喜欢用书本的目录来类比数据库的索引,并告诉咱们索引可以像目录同样,让咱们更快地找到想要找到的数据。数据库
若是是第一次接触索引,这个比喻可以让咱们有一个直观的印象。可是当深刻去学习索引的时候,咱们不能继续持有索引即目录的思想,咱们要跳出来去思考索引的本质是什么。数组
在没有索引的状况下,咱们查找数据只能按照从头至尾的顺序逐行查找,每查找一行数据,意味着咱们要到到磁盘相应的位置去读取一条数据。网络
若是把查询一条数据分为到磁盘中查询和比对查询条件两步的话,到磁盘中查询的时间会远远大于比对查询条件的时间,这意味着在一次查询中,磁盘io占用了大部分的时间。更进一步地说,一次查询的效率取绝于磁盘io的次数,若是咱们可以在一次查询中尽量地下降磁盘io的次数,那么咱们就能加快查询的速度。数据结构
在知道了减小磁盘io能加快查询速度后,咱们就要聚焦于如何减小磁盘io。若是按照原表逐行查询的话,n条数据就要查询n次,也就是O(N)的时间复杂度,为了减小磁盘io的次数,咱们必须用一种查询时间复杂度更低的数据结构来保存数据。性能
这种查询时间复杂度低的数据结构,咱们称之为索引。因此通俗来讲,索引其实就是某种数据结构,能充当索引的数据结构是多种多样的。学习
既然索引是一种便于查询的数据结构,若是你们对数据结构有必定了解的话,大几率会首选树型结构。毕竟树型结构广泛有着O(logN)的查询时间复杂度,并且插入删除数据的性能也比较平均。(可能你会说数组,哈希表的查询速度也很高啊,这个后面也会分析)大数据
虽然咱们都已经知道Mysql中最经常使用的引擎像InnoDB和MyISAM,最终都选择了B+树做为索引,可是这里我仍是打算从最多见的二叉树开始讲起,推导一下为何最终选择了B+树做为索引,并比较一下几种树型结构在充当索引时的优劣。优化
最普通的二叉树的问题在于他不能保证O(logN)的查询时间复杂度,咱们看下面的图:spa
因为插入的元素逐渐增大,元素始终在右边进行插入,好好的一棵二叉树最终变成了一条“链表”。在这种极端的状况下,二叉树的查询时间复杂度再也不是O(logN),而是退化为O(N),这样显然不符合索引的要求。
像红黑树这样的平衡二叉树,不管如何插入元素,他均可以经过一些旋转的方法调整树的高度,使得整棵树的查询效率维持在O(logN),以下图所示:
这么来讲他已经符合了成为索引的必备条件,可是最终没有选择他做为索引说明还有不足的地方。仔细看看能够发现平衡二叉树的每一个节点只有两个孩子节点,若是一张表的数据量特别大,整棵树的高度也会随之上升。一个千万级别的表若是用平衡二叉树做为索引的话,树高将会达到二十多层。这也就意味着作一次查询须要二十屡次磁盘io,这是一个不小的开销。
那么有没有能在大数据量的状况下,还能保持一个较小树高的树型结构呢?
答案就是B树。上面咱们说到了平衡二叉树的瓶颈在于一个节点只有两个孩子节点,而B树一个节点能够存放N个孩子节点,这就完美解决了树高的问题,咱们能够把B树称为平衡多叉树,B树做为索引以下图所示:
图片来源网络
可是以B树的结构做为索引仍有能够优化的地方,咱们先看看最终的B+树,再仔细分析B+树在B树的基础上做了哪些改进,为何B+树最终可以胜任索引的工做:
图片来源网络
从图片中能够看到B+树一样是一棵多叉平衡树,和B树同样很好地解决了树高的问题。
但仔细看能够发现,B树的节点中既存储索引,也存储表对应的数据;而B+树的非叶子节点是不存储数据的,只存储索引,数据所有存储在叶子节点上。
为何要作这样的改进?咱们作一次算术就知道了。
假设树高为2,主键ID为bigint类型,长度为8字节,节点指针为6字节,一行数据记录的大小为1k,一次io操做能得到一页16k的数据。
在索引为B+树的状况下,根节点能存储:16k / (6 + 8) = 1170 条索引指针;到了第一层,一共能指向 1170 * 1170 = 1368900 条索引指针;到了最底一层叶子节点,一个节点能存储16k / 1k = 16 条记录,一共能存储 1170 * 1170 * 16 = 21902400 条记录
在B树的状况下,因为非叶子节点使用了大量空间存储数据,存放的索引指针确定就少,最终整棵树若是想要存储和B+树同样多的数据就必需要增长树高,这样一来就增长了磁盘io,因此说B+树做为索引的性能比B树高。
叶子节点之间使用指针链接,提升区间访问效率。若是咱们要进行范围查询,能够轻松经过B+树叶子节点之间的指针进行遍历,减小了没必要要的磁盘io。
看到这里,相信你们对为何Mysql的经常使用引擎都默认使用B+树做为索引已经有了初步的认知。咱们只要牢记一点:索引是为了减小磁盘io提升查询性能而存在的。
最后回应一下为何不经常使用哈希表和数组做为索引
哈希表虽然单一个值的查询效率很高,可是撑不住范围查询,哪一个公司的业务还没个范围查询呢?
而数组虽然查询的效率高,可是增长和删除的效率低,因为记录在增长和删除的时候索引也得跟着维护,这会致使大数据量的状况下,增长或删除一条记录效率较低。