尬谈对B+树的个人理解

3,529 阅读5分钟

Today is the first day of the rest of your life. 
今天是你余下人生的第一天。

前言

金三银四,求职者越来越活跃,面试官问问题角度越来越刁钻。一言不合就问底层实现。大家都知道MySQL索引的实现用到了B+树,本文主要是分享了对B+树的一点个人理解。

为什么MySQL的索引要用B+树?

众所周知,查找效率比较高的数据结构有很多,呢为什么要用B+树呢?先简单分析一下查询效率比较高的几种数据结构。

散列表

散列表的查询性能很好,时间复杂度是 O(1)。但是,散列表不能支持按照区间快速查找数据。

跳表

跳表是在链表之上加上多层索引构成的。它支持快速地插入、查找、删除数据,时间复杂度是 O(logn)。并且,跳表也支持按照区间快速地查找数据。只需要定位到区间起点值对应在链表中的结点,然后从这个结点开始,顺序遍历链表,直到区间终点对应的结点为止,这期间遍历得到的数据就是满足区间值的数据。

ps:redis的有序集合就是跳表+散列表构建的

看着好像跳表也可以满足索引的要求。可能因为先有B+树后有的跳表,所以才选的B+?

平衡二叉查找树

平衡二叉查找树查询的性能高,时间复杂度是 O(logn)。而且,对树进行中序遍历,可以得到一个从小到大有序的数据序列,但这仍然不足以支持按照区间快速查找数据。

并且需要考虑一下磁盘的IO,数据索引是存在磁盘上的,当使用索引的时候,会把索引加到内存中去,(内存的访问速度完爆磁盘的访问速度)当数据足够大的时候,把索引完整的加入到内存当中,显然不现实。

B树

再来看B树,B树相比较平衡二叉查找树,它叉开的比较多,算是多叉平衡查找树。也就是说B树的高度要低,叉要多。如果把二叉查找树比做一个人,那么这个人是又高又有瘦,B树则是又矮又胖,某华真实写照,扎心了。。。

ps:树有多高,磁盘就读写多少次。还有mongodb的索引就用到了B。

B+树

B树和B+树其实是一种树,先由B树到B+树层层递进,逐步分析。

B树和B+树的区别

B树是一个自平衡的多叉查找树。它有以下优点:

  1. 保持键值有序,以顺序遍历
  2. 使用层次化的索引来最小化磁盘读取
  3. 使用不完全填充的块来加速插入和删除
  4. 通过优雅的遍历算法来保持索引平衡
  5. 通过保证内部节点至少半满来最小化空间浪费。
  6. 可以处理任意数目的插入和删除。

而B+树相当于B树的升级版,相对于B树来说B+树更充分的利用了节点的空间,让查询速度更加稳定,其速度完全接近于二分查找(二分查找速度不解释)。B+在基于B树的基础上又有如下优点:

  1. B+树的层级更少:B+树每个非叶子节点存储的关键字数更多,树的层级更少所以查询数据更快;
  2. B+树查询速度更稳定:B+所有关键字数据地址都存在叶子节点上,所以每次查找的次数都相同所以查询速度要比B树更稳定;
  3. B+树天然具备排序功能:B+树所有的叶子节点数据构成了一个有序链表,在查询大小区间的数据时候更方便,数据紧密性很高,缓存的命中率也会比B树高。
  4. B+树全节点遍历更快:B+树遍历整棵树只需要遍历所有的叶子节点即可,,而不需要像B树一样需要对每一层进行遍历,这有利于数据库做全表扫描。

总结

正因为B+树的这种特性,因此这种数据结构常用于文件系统跟数据库索引中。它通过对每个节点存储个数的扩展,使得对连续的数据能够进行较快的定位和访问,能够有效减少查找时间,提高存储的空间局部性从而减少IO操作。

B+树的特点

  • 每个节点中子节点的个数不能超过 m,也不能小于 m/2
  • 根节点的子节点个数可以不超过 m/2,这是一个例外
  • m 叉树只存储索引,并不真正存储数据,这个有点儿类似跳表
  • 通过链表将叶子节点串联在一起,这样可以方便按区间查找
  • 一般情况,根节点会被存储在内存中,其他节点存储在磁盘中

面试题:Mysql为什么用B+树而不用B树

BB了半天,重点来了。

1.IO对性能的影响

B树的每个节点都存储数据,而B+树只有叶子节点才存储数据,所以查找相同数据量的情况下,B树的高度更高,IO更频繁(通常就是高度=IO次数)。

2.从存储和使用方面考虑

数据库索引是存储在磁盘上的,当数据量过大时,就不能把整个索引全部加载到内存了,只能分片加载每一个磁盘页(对应索引树的节点)。

3.mysql底层的进一步优化

在叶子节点中是双向链表,且在链表的头结点和尾节点也是循环指向的。

end

您的点赞和关注是对我最大的支持,谢谢!