Mysql使⽤B+树原理
在进⼀步分析为什么MySQL数据库索引选择使⽤B+树之前,我相信很多⼩伙伴对数据结构中的树还是有些许模糊的,因此我们由浅⼊深⼀步步探讨树的演进过程,在⼀步步引出B树以及为什么MySQL数据库索引选择使⽤B+树!
学过数据结构的⼀般对最基础的树都有所认识,因此我们就从与我们主题更为相近的⼆叉查树开始。
⼀、⼆叉查树
(1)⼆叉树简介:
⼆叉查树也称为有序⼆叉查树,满⾜⼆叉查树的⼀般性质,是指⼀棵空树具有如下性质:
mysql面试题 知乎1、任意节点左⼦树不为空,则左⼦树的值均⼩于根节点的值;
2、任意节点右⼦树不为空,则右⼦树的值均⼤于于根节点的值;
3、任意节点的左右⼦树也分别是⼆叉查树;
4、没有键值相等的节点;
上图为⼀个普通的⼆叉查树,按照中序遍历的⽅式可以从⼩到⼤的顺序排序输出:2、3、5、6、7、8。
对上述⼆叉树进⾏查,如查键值为5的记录,先到根,其键值是6,6⼤于5,因此查6的左⼦树,到3;⽽5⼤于3,再其右⼦树;⼀共了3次。如果按2、3、5、6、7、8的顺序来同样需求3次。⽤同样的⽅法在查键值为8的这个记录,这次⽤了3次查,⽽顺序查需要6次。计算平均查次数得:顺序查的平均查次数为(1+2+3+4+5+6)/ 6 = 3.3次,⼆叉查树的平均查次数为(3+3+3+2+2+1)/6=2.3次。⼆叉查树的平均查速度⽐顺序查来得更快。边框素材 古风
css3特效背景(2)局限性及应⽤
⼀个⼆叉查树是由n个节点随机构成,所以,对于某些情况,⼆叉查树会退化成⼀个有n个节点的线性链。如下图:
⼤家看上图,如果我们的根节点选择是最⼩或者最⼤的数,那么⼆叉查树就完全退化成了线性结构。上图中的平均查次数为
(1+2+3+4+5+5)/6=3.16次,和顺序查差不多。显然这个⼆叉树的查询效率就很低,因此若想最⼤性能的构造⼀个⼆叉查树,需要这个⼆叉树是平衡的(这⾥的平衡从⼀个显著的特点可以看出这⼀棵树的⾼度⽐上⼀个输的⾼度要⼤,在相同节点的情况下也就是不平衡),从⽽引出了⼀个新的定义-平衡⼆叉树AVL。
⼆、AVL树
(1)简介
AVL树是带有平衡条件的⼆叉查树,⼀般是⽤平衡因⼦差值判断是否平衡并通过旋转来实现平衡,左右⼦树树⾼不超过1,和红⿊树相⽐,它是严格的平衡⼆叉树,平衡条件必须满⾜(所有节点的左右⼦树⾼度差不超过1)。不管我们是执⾏插⼊还是删除操作,只要不满⾜上⾯的条件,就要通过旋转来
保持平衡,⽽旋转是⾮常耗时的,由此我们可以知道AVL树适合⽤于插⼊删除次数⽐较少,但查多的情况。
从上⾯是⼀个普通的平衡⼆叉树,这张图我们可以看出,任意节点的左右⼦树的平衡因⼦差值都不会⼤于1。
(2)局限性
由于维护这种⾼度平衡所付出的代价⽐从中获得的效率收益还⼤,故⽽实际的应⽤不多,更多的地⽅是⽤追求局部⽽不是⾮常严格整体平衡的红⿊树。当然,如果应⽤场景中对插⼊删除不频繁,只是对查要求较⾼,那么AVL还是较优于红⿊树。
(3)应⽤
1、Windows NT内核中⼴泛存在;
三、红⿊树
(1)简介
⼀种⼆叉查树,但在每个节点增加⼀个存储位表⽰节点的颜⾊,可以是red或black。通过对任何⼀条从根到叶⼦的路径上各个节点着⾊的⽅式的限制,红⿊树确保没有⼀条路径会⽐其它路径长出两倍。它是⼀种弱平衡⼆叉树(由于是若平衡,可以推出,相同的节点情况
下,AVL树的⾼度低于红⿊树),相对于要求严格的AVL树来说,它的旋转次数变少,所以对于搜索、插⼊、删除操作多的情况下,我们就⽤红⿊树。
(2)性质
1、每个节点⾮红即⿊;
代码overflow什么意思2、根节点是⿊的;
3、每个叶节点(叶节点即树尾端NULL指针或NULL节点)都是⿊的;
4、如果⼀个节点是红的,那么它的两⼉⼦都是⿊的;
5、对于任意节点⽽⾔,其到叶⼦点树NULL指针的每条路径都包含相同数⽬的⿊节点;
6、每条路径都包含相同的⿊节点;
(3)应⽤
1、⼴泛⽤于C++的STL中,Map和Set都是⽤红⿊树实现的;
2、著名的Linux进程调度Completely Fair Scheduler,⽤红⿊树管理进程控制块,进程的虚拟内存区域都存储在⼀颗红⿊树上,每个虚拟地址区域都对应红⿊树的⼀个节点,左指针指向相邻的地址虚拟存储区域,右指针指向相邻的⾼地址虚拟地址空间;
3、IO多路复⽤epoll的实现采⽤红⿊树组织管理sockfd,以⽀持快速的增删改查;
4、Nginx中⽤红⿊树管理timer,因为红⿊树是有序的,可以很快的得到距离当前最⼩的定时器;
5、Java中TreeMap的实现;
四、B/B+树
说了上述的三种树:⼆叉查树、AVL和红⿊树,似乎我们还没有摸到MySQL为什么要使⽤B+树作为索引的实现,不要急,接下来我们就先探讨⼀下什么是B树。
(1)简介
我们在MySQL中的数据⼀般是放在磁盘中的,读取数据的时候肯定会有访问磁盘的操作,磁盘中有两个机械运动的部分,分别是盘⽚旋转和磁臂移动。盘⽚旋转就是我们市⾯上所提到的多少转每分钟,⽽磁盘移动则是在盘⽚旋转到指定位置以后,移动磁臂后开始进⾏数据的读写。那么这就存在⼀个定位到磁盘中的块的过程,⽽定位是磁盘的存取中花费时间⽐较⼤的⼀块,毕竟机械运动花费的时候要远远⼤于电⼦运动的时间。当⼤规模数据存储到磁盘中的时候,显然定位是⼀个⾮常花费时间的过程,但是我们可以通过B树进⾏优化,提⾼磁盘读取时定位的效率。
为什么B类树可以进⾏优化呢?我们可以根据B类树的特点,构造⼀个多阶的B类树,然后在尽量多的在结点上存储相关的信息,保证层数尽量的少,以便后⾯我们可以更快的到信息,磁盘的I/O操作也少⼀些,⽽且B类树是平衡树,每个结点到叶⼦结点的⾼度都是相同,这也保证了每个查询是稳定的。
总的来说,B/B+树是为了磁盘或其它存储设备⽽设计的⼀种平衡多路查树(相对于⼆叉,B树每个内节点有多个分⽀),与红⿊树相⽐,在相同的的节点的情况下,⼀颗B/B+树的⾼度远远⼩于红⿊树的⾼度(在下⾯B/B+树的性能分析中会提到)。B/B+树上操作的时间通常由存取磁盘的时间和CPU计算时间这两部分构成,⽽CPU的速度⾮常快,所以B树的操作效率取决于访问磁盘的次数,关键字总数相同的情况下B树的⾼度越⼩,磁盘I/O所花的时间越少。
注意B-树就是B树,-只是⼀个符号。汇编指令寄存器清零
(2)B树的性质
1、定义任意⾮叶⼦结点最多只有M个⼉⼦,且M>2;
2、根结点的⼉⼦数为[2, M];
3、除根结点以外的⾮叶⼦结点的⼉⼦数为[M/2, M];
4、每个结点存放⾄少M/2-1(取上整)和⾄多M-1个关键字;(⾄少2个关键字)
5、⾮叶⼦结点的关键字个数=指向⼉⼦的指针个数-1;
6、⾮叶⼦结点的关键字:K[1], K[2], …, K[M-1];且K[i] < K[i+1];
7、⾮叶⼦结点的指针:P[1], P[2], …, P[M];其中P[1]指向关键字⼩于K[1]的⼦树,P[M]指向关键字⼤于K[M-1]的⼦树,其它P[i]指向关键字属于(K[i-1], K[i])的⼦树;
8、所有叶⼦结点位于同⼀层;
这⾥只是⼀个简单的B树,在实际中B树节点中关键字很多的,上⾯的图中⽐如35节点,35代表⼀个key(索引),⽽⼩⿊块代表的是这个key 所指向的内容在内存中实际的存储位置,是⼀个指针。
五、B+树
(1)简介
B+树是应⽂件系统所需⽽产⽣的⼀种B树的变形树(⽂件的⽬录⼀级⼀级索引,只有最底层的叶⼦节点(⽂件)保存数据)⾮叶⼦节点只保存索引,不保存实际的数据,数据都保存在叶⼦节点中,这不就是⽂件系统⽂件的查吗?
我们就举个⽂件查的例⼦:有3个⽂件夹a、b、c, a包含b,b包含c,⼀个⽂件yang.c,a、b、c就是索引(存储在⾮叶⼦节点), a、b、c只是要到的yang.c的key,⽽实际的数据yang.c存储在叶⼦节点上。
所有的⾮叶⼦节点都可以看成索引部分!
(2)B+树的性质(下⾯提到的都是和B树不相同的性质)
1、⾮叶⼦节点的⼦树指针与关键字个数相同;
2、⾮叶⼦节点的⼦树指针p[i],指向关键字值属于[k[i],k[i+1]]的⼦树.(B树是开区间,也就是说B树不允许关键字重复,B+树允许重复);
3、为所有叶⼦节点增加⼀个链指针;
4、所有关键字都在叶⼦节点出现(稠密索引). (且链表中的关键字恰好是有序的);
5、⾮叶⼦节点相当于是叶⼦节点的索引(稀疏索引),叶⼦节点相当于是存储(关键字)数据的数据层;
种子z型提升机6、更适合于⽂件系统;
⾮叶⼦节点(⽐如5,28,65)只是⼀个key(索引),实际的数据存在叶⼦节点上(5,8,9)才是真正的数据或指向真实数据的指针。
(3)应⽤
1、B和B+树主要⽤在⽂件系统以及数据库做索引,⽐如MySQL;
六、B/B+树性能分析
n个节点的平衡⼆叉树的⾼度为H(即logn),⽽n个节点的B/B+树的⾼度为logt((n+1)/2)+1;
若要作为内存中的查表,B树却不⼀定⽐平衡⼆叉树好,尤其当m较⼤时更是如此。因为查操作CPU的时间在B-树上是
O(mlogtn)=O(lgn(m/lgt)),⽽m/lgt>1;所以m较⼤时O(mlogtn)⽐平衡⼆叉树的操作时间⼤得多。因此在内存中使⽤B树必须取较⼩的m。(通常取最⼩值m=3,此时B-树中每个内部结点可以有2或3个孩⼦,这种3阶的B-树称为2-3树)。
七、为什么说B+树⽐B树更适合数据库索引?
1、 B+树的磁盘读写代价更低:B+树的内部节点并没有指向关键字具体信息的指针,因此其内部节点相对B树更⼩,如果把所有同⼀内部节点的关键字存放在同⼀盘块中,那么盘块所能容纳的关键字数量也越多,⼀次性读⼊内存的需要查的关键字也就越多,相对IO读写次数就降低了。
2、B+树的查询效率更加稳定:由于⾮终结点并不是最终指向⽂件内容的结点,⽽只是叶⼦结点中关键字的索引。所以任何关键字的查必须⾛⼀条从根结点到叶⼦结点的路。所有关键字查询的路径长度相同,导致每⼀个数据的查询效率相当。
3、由于B+树的数据都存储在叶⼦结点中,分⽀结点均为索引,⽅便扫库,只需要扫⼀遍叶⼦结点即可,但是B树因为其分⽀结点同样存储着数据,我们要到具体的数据,需要进⾏⼀次中序遍历按序来扫,所以B+树更加适合在区间查询的情况,所以通常B+树⽤于数据库索引。
PS:我在知乎上看到有⼈是这样说的,我感觉说的也挺有道理的:
他们认为数据库索引采⽤B+树的主要原因是:B树在提⾼了IO性能的同时并没有解决元素遍历的我效率低下的问题,正是为了解决这个问题,B+树应⽤⽽⽣。B+树只需要去遍历叶⼦节点就可以实现整棵树的遍历。⽽且在数据库中基于范围的查询是⾮常频繁的,⽽B树不⽀持这样的操作或者说效率太低。
今天看了⼏篇⽂章,⾃⼰总结⼀下。
数据库使⽤B+树肯定是为了提升查效率。
但是具体如何提升查效率呢?
查数据,最简单的⽅式是顺序查。但是对于⼏⼗万上百万,甚⾄上亿的数据库查询就很慢了。
所以要对查的⽅式进⾏优化,熟悉的⼆分查,⼆叉树可以把速度提升到O(log(n,2)),查询的瓶颈在于树的深度,最坏的情况要查到⼆叉树的最深层,由于,每查深⼀层,就要访问更深⼀层的索
引⽂件。在多达数G的索引⽂件中,这将是很⼤的开销。所以,尽量把数据结构设计的更为‘矮胖’⼀点就可以减少访问的层数。在众多的解决⽅案中,B-/B+树很好的适合。B-树定义具体可以查阅,简⽽⾔之就是中间节点可以多余两个⼦节点,⽽且中间的元素可以是⼀个域。相⽐B-树,B+树的⽗节点也必须存在于⼦节点中,是其中最⼤或者最⼩元素,B+树的节点只存储索引key值,具体信息的地址存在于叶⼦节点的地址中。这就使以页为单位的索引中可以存放更多的节点。减少更多的I/O⽀出。因此,B+树成为了数据库⽐较优秀的数据结构,MySQL中MyIsAM和InnoDB都是采⽤的B+树结构。不同的是前者是⾮聚集索引,后者主键是聚集索引,所谓聚集索引是物理地址连续存放的索引,在取区间的时候,查速度⾮常快,但同样的,插⼊的速度也会受到影响⽽降低。聚集索引的物理位置使⽤链表来进⾏存储。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论