咱们用了这么久Mysql数据库做项目,你知道数据是怎么存在数据库里吗?他们是如何存储的吗?
今天咱们就来扒一扒Mysql数据库索引的底层实现,Mysql数据库的索引是由都是由B+树实现的,那为什么不是其他的数据结构呢,比如二叉树,链表或者数组什么的?今天我们就来一探究竟。
0.前提
首先要想加快查找的速度,一个不管用什么存储,他的前提是一定要有序的,这个应该理解吧。如果数据的存储是无序的,任何数据类型的存储,在查找时都会降维成O(n),所以有序是大前提。
数组
先分析一下,数组是在内存怎么存储的?
思考一下
。
。
。
数组是在内存中连续存储的,它是有固定长度的,所以他不能扩容,初始申请的长度多长就是多长。因为连续存储,所以只要获取到数组的位置,你就知道了数组的所有元素的位置,这个应该理解吧?所以随机查询速度很快。但是如果你要是插入和删除元素的时候,因为内存的连续性,所以插入和删除时为了保持数组的特性(内存的连续性),就需要挪动元素。总的来说,数据插入和删除比较慢,查询比较快。但是作为数据库动辄百万千万的数据,插入一个数据,如果需要挪动百万次,这个数据库你觉得能用吗?
所以呢,数组Out!
链表
然后咱们再来看下链表
链表是怎么存储的?
思考一下
。
。
。
链表在内存里是不连续存储的,每一个链表元素都有一个指针(next),指向下一个元素的位置,即便链表的加强版双向链表,也只有你轮寻到它(要找的元素)的上一个或下一个元素,才知道它的位置。所以插入和删除是不用挪动了,但是查找又是大问题,要查找一个元素最慢要O(n/2).
所以呢,链表out!
二叉树
再来分析一下二叉树
二叉树是每个节点有零到两个子节点,并且左右节点是有序的,左边的都比根节点小,而右节点都比根节点大,这样每次查找都减少一半的无效数据,相当于二分查找,他的查找速度是O(log2 n),虽然他相对于其他的来说查找速度已经够快了,但是当进行范围查找时,他还是支持的不够好。离正确答案已经接近了,但还是有一段距离。
所以呢,二叉树,out!
那数据库到底怎么存在磁盘上,才能让查找速度又快,又支持范围查找呢?且听我娓娓道来。
思考下,数据库是如何存储呢?
。
。
。
。
因为数据库是可以持久化的,所以他一定是存储在硬盘上的,然而,从硬盘读取数据是相对内存来说是极度缓慢的,一次IO大概在9ms左右,它的成本大概是内存的10万倍。也正是因为磁盘IO的昂贵,所以操作系统对此做了优化,也就是预读;每一次可以读取一个数据时,他相邻的数据也一起读取出来。
看一下原理,局部预读原理:
当访问一个地址数据的时候,与其相邻的数据很快也会被访问到。每次磁盘IO读取的数据我们称之为一页(page)。一页的数据,根据操作系统不同,一页是4k或者8k。也就是说,每读一页时,就发生了一次IO。
也正是因为磁盘有预读机制的缘故,所以才能有进一步减少磁盘IO的可能性,也就是查询更快的可能,这里先卖个官司,请客官接着往下看。
磁盘的存储是线性内存地址(见图1)
硬盘是由很多的内存空间组成的,内存空间是连续的,内存空间包含两部分,第一部分是内存地址,第二部分是当前内存的值。
1.非平衡二叉树
接着二叉树的分析
操作系统的内存是连续的存储的,寻址就是很耗时的,你可以把它理解成一个数组的,当我们对数组进行,查找的时候二分查找的速度应该是最快的,因为理论上每一次查找都能筛掉大概一半的无效的数据,二分查找就相当于非平衡二叉树查找,所以二分查找的速度会因为选择的中间变量的位置来决定。
如果极端情况,会造成n次查找(见图3)。因为数据库要求稳定性,即每一次查找的速度都差不多。就好比如果你上某宝查询商品时,如果遇到一次这种情况,你就直接口吐芬芳了,随即怒卸之!所以为了让用户留下来,我们要让每次查找都差不多。
所以,非平衡二叉树,out!继续寻找。
2.平衡二叉树
平衡二叉树(常用算法实现有红黑树),顾名思义,就是每个节点的两边比较平衡,它的特征是根节点是空树,或者每个节点的左右子树的节点数相差不超过1,因为当每次添加一条数据时,他就会添加一个子节点,然后他会自旋转,把左右子树调整为差值小于等于1,即每次查找的时间都是O( log2 N)。所以他的查找也比较稳定。因为一个箭头都是一次寻址过程,都比较耗时,并且如果总数据慢慢增多,查询效率下降的还是比较快。那怎么才能更快些呢?然后就有了B树。
3.B树
每个节点只存一节点显然已经不能更快了,那怎么办呢?
B树来了,B树就是N阶二叉树,N阶限制的是每个节点最多有N个子树。然后因为每个节点能存多个值,N阶B树的特点就是,每个节点最多有N个子节点,并且每个节点可存多个值(这样设计,也是因为磁盘的预读机制,每次查一页数据),通过每个节点存多个值,所以树变得更矮胖,也就是IO次数更少,更快的查找到。当删除节点11时,就会自动左旋,让树变得平衡,以此来减少对比的次数,保证查询的速度最快。
查询速度已经很快了,但是如果你要进行范围查询就比较麻烦了。
比如要做查询该值大于3并且小于13的数据,如果用B树查找是这样的(图7)
1.要先找到3所在的位置
2.找到13算在的位置
3.找到3以后的元素,在回退到上一层,看有没有右节点,再回退上一层,再看看有没有右节点。
4.一直到13的位置的左节点的位置。
Ps.看看我这个10来条数据,来一个范围查找最少最少要5次IO,IO是很昂贵的,所以就有了B+树。
4.B+树
B+树的特点是,所有data都存在叶子节点上,非叶子节点上不存data,只是做索引。然后叶子节点是一个连续的双向链表,树的深度没有增加,也就是查询速度没有变慢多少,但是它的范围查找时的速度是大大的提高。
来看一下B+树的范围查找,还是刚才的查找
要做查询该值大于3小于13的数据,用B+树查找的步骤
1.查找到3的位置,一直往后,查到13停止
what?这TM也太快了吧!只要找到3所在的节点,就基本不需要IO了,飞一般的感觉!嗖嗖的!
总结
最后总结下,B+树是兼顾了查询的速度和范围查找的速度的终极产物,所以好多数据库的索引都使用了B+树来实现。
今天是大年初五,慕容田雨祝大家新春快乐,万事如意,阖家欢乐,鼠年大吉!