数据结构对决:B+树如何在MySQL索引中胜出

数据库索引基础

在开始讨论B+树之前,我们首先要知道什么是索引,索引到底是干什么的,为什么要有索引?

首先,索引的目的在于提高查询效率,比如我们在查字典的时候,如果要查 “abcd” 这个单词,我们要先定位到 a 字母,然后在所有 a 开头的单词中找第二个字母为 b 的单词,以此类推直到找到整个单词。如果没有目录,那么你可能需要把所有单词看一遍才能找到你想要的,如果这个字典里有几十万个单词呢,几百万甚至几千万呢? 如果没有目录这个事情根本无法完成。

对于数据库的表而言,索引其实就是它的“目录”。

B+树与其他数据结构的比较

哈希表

说起目录与查询,我们首先想到的一定是哈希表,哈希表可以通过哈希函数快速定位数据,在 O(1)的时间复杂度下完成查找、插入和删除操作。但是如果我们要进行模糊查询的话,却只能遍历所有数据,并且在极端情况下,哈希表冲突的元素太多会导致查找效率退化到 O(n),这也是HashMap 树化的原因。

二叉搜索树

二叉搜索树是一种有序的二叉树,其中每个节点的键值小于其左子树所有节点的键值,大于或等于其右子树所有节点的键值,可以在 O(log n) 的时间复杂度下完成查找、插入和删除操作。但是和哈希表一样,二叉搜索树在极端的情况下会退化为链表,比如树的高度与节点数相同,这个时候操作的时间复杂度就会退化为 O(n) 。因此,保持二叉查找树的平衡对于其性能至关重要。

AVL树与红黑树

如何保持二叉搜索树的平衡呢?这里就引出了AVL树,AVL树是一种自平衡的二叉搜索树,它通过在每次插入或删除节点后进行旋转操作来保持树的平衡性。其特点是任何节点的两个子树的高度最大差别为 1 ,这种平衡性确保了树的查找、插入和删除操作都能在 O(log n) 的时间复杂度内完成。

上文提到了HashMap的树化,那么HashMap树化为什么会化为红黑树而不是AVL树呢?

原因在于,AVL树虽然因为严格的平衡性使其查询效率稳定在 O(log n),但是为了维护这种平衡,它会在删除或者插入数据之后进行很多旋转操作,这种操作导致了它的性能下降,而红黑树是一种弱平衡树,只需要确保树的高度大致平衡即可,它牺牲了一定的严格平衡性以减少旋转操作的次数,在插入和删除操作上比AVL树有更高的性能。

B 树

如果只是普通的增删改查,红黑树已经可以说是最优解了,但是我们的文件索引是存放在磁盘上的,所以我们不仅要考虑查找效率,还要考虑磁盘的IO,这个时候就引出了 B 树。

B树是一种自平衡的多路查找树,它能够保持数据有序,并且允许在 O(log n) 时间内完成查找、顺序访问、插入和删除操作。B 树是二叉搜索树的一般化,可以拥有多于两个子节点,特别适合用于外部存储系统如数据库和文件系统,以优化大块数据的读写操作。
在这里插入图片描述
如上图所示,图中是一棵 3 阶 B 树,和二叉查找树一样,左节点的所有元素的值都比父亲元素小。例如对于(4, 7)这个节点。两个元素把这个节点分割成三个值域,即可以有 3 个孩子。3 相当于 4 的左孩子节点,而 (5,6)相当于 4 的右孩子,同时也是 7 的左孩子,而 (8,11,12) 是 7 的右孩子。

假设我们现在要查询元素 8,那么我们只需要进行 3 次磁盘 IO 即可完成,但是换成二叉平衡树,显然就需要更多的 IO 次数了,虽然在这个过程中 B 树进行了更多的比较次数,但是在进行数值比较的时候是在内存中进行的,至少比磁盘的速度快了几百倍,B 树的比较次数可能比二叉查找树多,但是磁盘操作次数少,所以总体来说还是 B 树快的多。

实际上磁盘的 IO 次数是和树的高度相关联的,所以对于这种文件系统和数据库索引的存储,我们一般会选择这种矮胖的树形结构。

B+ 树

既然 B 树已经这么优秀了,为什么还是没有采用呢?

我们先来看看 B+ 树的结构

在这里插入图片描述

从上图可以看到,B+ 树非叶子节点仅存储索引信息,不存储实际的数据记录,因此每个结点可以存储更多的索引信息,相比 B 树进一步的降低了树高,相对来说 IO 读写次数也就降低了,磁盘读写代价更低。

由于非叶子结点只是指向叶子结点中关键字的索引,所以任何关键字的查找必须走一条从根结点到叶子结点的路,所有关键字查询的路径长度相同,使得每一个数据的查询效率更加稳定。

另外,B 树在提高了磁盘 IO 性能的同时并没有解决元素遍历的效率低下的问题,而 B+ 树由于其叶子结点之间以双向链表连接,只要遍历叶子节点就可以实现整棵树的遍历。

B+ 树索引的磁盘 IO 优化

MySQL 的数据保存在磁盘上,而磁盘读取数据靠的是机械运动,每次读取数据花费的时间可以分为寻道时间、旋转延迟、传输时间三个部分。
在这里插入图片描述

寻道时间指的是磁臂移动到指定磁道所需要的时间,即磁头从开始移动到数据所在磁道所需要的时间,寻道时间越短,I/O操作越快,目前磁盘的平均寻道时间一般在3 - 15 ms,我们取平均值以9 ms 记录。

旋转延迟是指盘片旋转将请求数据所在的扇区移动到读写磁盘下方所需要的时间。旋转延迟取决于磁盘转速,最坏的情况下是磁盘旋转一周所需时间的 1 / 2 ,即刚好转了 180 °。一般 7200 rpm 的磁盘平均旋转延迟大约为60 * 1000 / 7200 / 2 = 4.17 ms。

传输时间指的是从磁盘读出或将数据写入磁盘的时间,一般在零点几毫秒,相对于前两个时间可以忽略不计。

那么访问一次磁盘的时间,即一次磁盘 IO 的时间约等于 9 + 4.17 约 13 ms左右,听起来还挺快的,但是数据库动辄十万百万乃至千万级数据,每次 13 ms 的时间显然是不能接受的。

那么要优化磁盘的 IO ,我们能想到的有两点:减少 IO 次数 和 减少 IO 时间

B+ 树由于其特殊的结构,三层的 B+ 树就能存下上百万的数据,极大的优化了磁盘 IO 的次数,那么如何减少 IO 的时间呢?

上文提到

旋转延迟是指盘片旋转将请求数据所在的扇区移动到读写磁盘下方所需要的时间。旋转延迟取决于磁盘转速,最坏的情况下是磁盘旋转一周所需时间的 1 / 2

那么怎么才能避免这种最坏的情况呢,答案就是顺序读取,由于所有叶子节点形成了一个链表,B+树可以定位到查询范围的起始点,然后顺序访问直到结束,这样就减少了旋转延迟,很好的减少了单次 IO 花费的时间。

  • 24
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值