数表的查找之 MySql 为什么使用 B+ 树?

数表的查找之 MySql 为什么使用 B+ 树?

讲解之前,先进行思考:

思考:从磁盘查找数据效率低,一般是什么原因?

我们知道 CPU 速度非常快,如果 CPU 直接与磁盘进行交互,由于磁盘的 IO 速度较慢的限制,因此不能直接使 CPU 与磁盘交互,而是在之间引入内存,先将磁盘中的数据加载至内存,CPU 再从内存中读取数据。

在这里插入图片描述

解释到这里,继续回归先前的思考题,如果从磁盘查找数据消息低,那么一定是出现在磁盘的 IO 上。

影响 IO 效率的因素主要有两大类:

  • 读写数据越大,速度越慢;
  • 读写次数越多,速度越慢;

对于读写的数据大,这里没什么改变的办法,有时候的需求确实是读取大量的数据,但是对于读写的次数多,这里可以进一步优化——引入索引。


方式一:线性查找

对于线性查找,是最简单也是最原始的查找方式,它的缺点就是,查找效率非常低,对于每一次查找,都需要遍历全部数据,查询效率非常低。

在这里插入图片描述


方式二:哈希查找

既然线性查找效率非常低下,那么我们引入哈希表,通过元素值的 key 去查找对应的分支链表,这样查询效率有所提升,但是,哈希查找也存在弊端,因为哈希查找存在哈希冲突,一旦出现哈希冲突,极端的情况下就会出现一条单链表,时间复杂度还是O(N);对于哈希冲突,内部有很多解决哈希冲突的算法,也不是最本质的原因,最本质的原因是哈希查找不支持范围查询,因此也不采用不采用哈希查找。

在这里插入图片描述

方式三:引入树查找—二叉树

对于二叉树,由于每个节点的值没有规律,查询起来还是需要一个个查找对比,还是很费劲,时间复杂度还是O(N),因此二叉树也不行。

在这里插入图片描述

方式四:二叉树改造之二叉排序树(BST)

既然二叉树的缺点是节点之间没有规律,那么换成二叉排序树怎么样?左子树永远比根节点小,右子树永远比根节点大。咋一看好像可以,但是仔细想想还是存在特殊情况:只有左子树或只有右子树。这样的时间复杂度依旧是O(N)。

在这里插入图片描述

方式五:二叉排序树(BST)改造之平衡二叉树(AVL)

既然二叉排序树会存在只有左子树或右子树的极端情况,那么我们引入平衡二叉树不就解决了?让树的形态永远保持平衡。但是啊,平衡二叉树的每一次调整平衡,都需要耗时,类似于是在用插入数据时的成本,来换取查询的效率。如果插入少查询多,那么这种方法也可取,但是插入多查询少时,就不可取了。

在这里插入图片描述

方式六:平衡二叉树(AVL)改造之红黑树

红黑树:红黑树是一颗特殊的二叉树,缺点也是高度不可控。

对于红黑树,在插入的时候虽然也需要进行红黑节点的调整,但是相对于平衡二叉树也有所好转。是不是红黑树就满足我们的需求了呢?假如我们插入了大量的数据,那么红黑树的深度也会特别深,树的深度越深,查找次数就会变多,因此效率也没有得到本质的改善。

在这里插入图片描述

方式七:红黑树改造之降低树的深度(B 树)

B 树概念:满足下列的 m 叉树就是一颗 B 树(B-Tree)不是 B- 树:

(1)树中每个结点至多有 m 个孩子结点(即最多只有 m-1 个关键字)。
(2)每个结点的结构为:

在这里插入图片描述
n 表示关键字及孩子个数,p表示指向孩子结点的指针,k表示关键字。

(3)除根节点外,其他结点至少有 m/2 个孩子结点。
(4)若根节点不是叶子节点,则根节点至少有两个孩子结点。
(5)所有叶子结点都在同一层上,即 B 树是所有结点的平衡因子都等于 0 的多路查找树。

在这里插入图片描述

既然二叉树无法避免树的深度急剧加深,那么我们就想办法减少树的深度,因此引入多叉树如何?(引入B树,B树就是一个有序的多路查找树),树的叉数变多后,对于同一组数据,深度自然就会降低。

此处先引入一个名词:磁盘预读。

  • 内存跟磁盘发生数据交互的时候,一般情况下有一个最小的逻辑单元,称之为页:datapage。
  • 页一般由操作系统决定是多大,一般是 4k 或者 8k( mysql 默认是16k),在进行数据交互的时候,可以取页的整数倍来进行读取。

对于 B 树的每一个结点,都是放在一个 datapage 里面,每次查找数据时,都是先从磁盘中 IO 读取一个 datapage,然后依次往下查找,直到找到为止。假设一个结点的大小是16k(mysql 默认是16k)。

B 树笼统的说,就是一颗平衡多路的查找树(或者说是平衡多叉树),可以有效的降低树的深度,但是对范围查询很不友好,因此 mysql 索引结构没有采用 B 树。


思考并计算:B+ 树到底有多高?

在 InnoDB 存储引擎中,B+ 树索引这棵索引树上每个非叶子节点都由 key 和指针组成(key存储的是主键),一个节点的大小为16KB、一个指针的大小为 6 字节,这都是固定的。

假设主键采用 bigint 进行存储(按最大值计算)也就是 8 字节,也假设数据库表的每行数据都是 1KB,也就是一个叶子节点可以存储 16 行数据。

由于每个非叶子节点中指针的个数都比 key 多 1 个,因此对于每个节点,设 n 为 主键个数,得出计算公式:

n * 8 +(n + 1) * 6 = 16 * 1024,n ≈ 1170(个)

  • 对于高度为 2 的 B+ 树,(1170 + 1) * 16 = 18736(行)
  • 对于高度为 3 的 B+ 树,(1170 + 1) * (1170 + 1) * 16 = 21939856(行)

可以明显得看出,高度为 3 的 B+ 树就可以存储 2000 多万行数据行,而树的高度越低,查询的时候效率也就越高。


扩展:终结操作之 B+ 树

模拟构建 B+ 树网址:https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html

在这里插入图片描述

对于 B+ 树和 B 树,有以下区别:

  • 非叶子节点不存储数据,只存储索引元素,这就使得非叶子节点可以存储更多的索引元素,可以有效的降低树的高度;
  • 所有的数据,都存储在叶子节点上,并且在叶子节点之间形成一颗双向的循环链表,可以友好的支持范围查询;
  • 相比 B 树来说,进行范围查询时 B+ 树只需要查找两个节点,进行遍历即可,而 B 树需要获取所有节点,相比之下 B+ 树效率更高,这也是采用 B+ 树最大的优点;

总结 mysql 使用 B+ 树的原因

  • 数据都存储在叶子节点,因此非叶子节点就可以存储更多的索引元素,因此整颗 B+ 树会变得更矮更胖,因此查询数据的时候磁盘 IO 会更少,查询效率会提高;
  • 所有数据都存储在叶子节点上,并且形成一颗双向的循环链表,这样使得范围查找、排序查找特别方便,这也是最大的优势;




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值