MySQL笔记(七):MySQL 索引底层为什么选择B+Tree

MySQL索引

      索引是为了加速对表中数据行的检索而创建的一种分散存储的数据结构。索引的建立对于MySQL的高效运行是很重要的,索引可以大大提高MySQL的检索速度。

为什么要使用索引

  1. 索引能够大大的减少存储引擎需要扫描的数据量,无索引则进行全表扫描;
  2. 索引可以把随机IO变成顺序IO(因为IO操作很耗时);
  3. 索引可以帮助我们在进行分组、排序等操作时,避免使用临时表

MySQL索引底层为什么选择 B+Tree

       接下来,我们将从 二叉树平衡二叉树B-TreeB+Tree 等方面来分析,了解 MySQL 索引底层为什么会选择 B+Tree 。

       :提供一个很好用的国外数据结构模拟网站:我是链接

1.二叉树 (Binary Search Tree)

     二叉树第一次插入的节点,我们称之为根节点,如下图中 10 便是一个根节点。

     接下来,我们从两个场景来分析 二叉树的使用

     场景1:如下图二叉树,如果我们现在要检索 7 ,则是先到达根节点,其值为10,7小于10,那么我们就可以过滤掉【10右边】的数据,在左子树上找,到达5,7大于5,则过滤掉【5左边】的数据,在右子树上找,最终检索到 7。

       如果我们现在要检索 8 ,则是先到达根节点,其值为10,7小于10,那么我们就可以过滤掉【10右边】的数据,在左子树上找,到达5,7大于5,则过滤掉【5左边】的数据,在右子树上找,而值为7的节点的右子树是空的,因此该检索二叉树中不存在值为8的节点。

在这里插入图片描述
     场景2:如下图二叉树,根节点为5,如果接下来每次插入的数据都比根节点大,那么最终出来的数据就是线性链表的结果。如果现在我们从这个数据结构中去检索id = 48 的数据。层层检索,那么 48 需要检索7次,我们会发现,这种链表样式的二叉树检索和全表扫描没有太大的区别。
在这里插入图片描述
       总结我们发现二叉树最终的检索效率,其实是取决于数据的分布情况。如果数据分布均匀,还是能提高一定的查询效率的;数据如果分布不均匀,用二叉树来做索引的话,还是存在着一定的不足性。

2.平衡二叉树 (Balanced binary search trees)

       平衡树,即平衡二叉树(Balanced Binary Tree),它具有以下性质:它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树。

       平衡二叉树的常用算法有红黑树、AVL、Treap、伸展树、SBT等。


       首先我们先来了解一下 高度差这个概念,如下图所示:
在这里插入图片描述
        右旋操作,如下图所示:
在这里插入图片描述


       通过以上分析,平衡二叉树的出现,它可以帮助我们解决二叉树的不足之处。平衡二叉树,它会通过在数据的插入、删除等情况下,来维持这棵二叉树的相对平衡。这一方面,它能够解决二叉树线性链表的问题。

使用平衡二叉树作为索引,如何检索数据?

在这里插入图片描述

1.磁盘块分析

     一个节点就是一个磁盘块,一个磁盘块保存有如下内容:

  1. 关键字:比如说我们创建 ID 为索引,ID=15,那么15 就是一个关键字。
  2. 数据区:指向真正数据存储的磁盘位置(内存地址),通过数据区去加载数据。一般情况下,为了节省空间,数据区是不会保存数据的,而是指向磁盘的一个位置,然后去磁盘把内容加载过来,达到快速检索的目的。
  3. 子节点引用:指向子节点的引用。

2.如果现在我们要检索 12,流程如下:

  1. 首先将磁盘块1加载到内存中,获取到根节点数据。让 12 和 15 这个关键字进行比对,12<15,会基于 P1 (P1、P2是指向下一个磁盘块的地址),通过顺序 IO 快速的去加载磁盘块 2 中的内容;
  2. 12 和 10 继续比对,12 > 10,走P2 ,加载磁盘块 5,然后命中 12。命中后,获取到数据区地址,再次加载磁盘中的数据。

平衡二叉树,还存在哪些缺陷、问题?

     平衡二叉树,看着没有啥问题。但是 MySQL 索引还是没用平衡二叉树,因为平衡二叉树还是存在如下一些问题的:

  1.它太深了

      数据所在的(高)深度决定着它的 IO 操作次数,IO 操作耗时比较大。

  2.它太小了

      每一个磁盘块(节点/页),保存的数据量太小了,没有很好的利用操作磁盘 IO 的数据交换特性,也没有利用好磁盘 IO 的预读能力(空间局部性原理),从而带来频繁的 IO 操作。

解析:

     太深了:比如说我们检索 12,需要做 3 次 IO 操作获得数据。(3层高度只能存放 7 个数据)如果我们数据达到亿级别,那么这棵二叉树会有多大,我们无法想象。然而IO 操作是很费时的,显然无法满足条件。
     太小了:通过N多次 IO 操作后,拿到的数据太少了
     没能很好的利用数据交换特性: 传统I/O 操作,是以一页数据为单位,一次交换的数据是大约为4K。然而(关键字+数据区+子节点引用)这些数据显然是填充不满 4K 的。如果一次加载回来的数据很少,排除用不到的数据后,我们能用到的数据就更少了。二叉树又那么高,IO 操作那么多还那么费时,检索数据会做很多无用功。
     没有很好的利用磁盘IO的预读能力: 操作系统在IO操作时,比如说要读 2M txt 文件。先把头部 4k 读回来后,它会认为你马上就会使用接下来的 4k 数据(举个栗子:比如你读书,在读完第1页时,你会马上去读第2页。操作系统提供的空间局部性原理,就是在你读第1页的同时,它会去把第 2 页的内容给你提前加载过来,就是提前加载的意思)。真正做一次数据交换,并不仅仅是做一次 4K 数据量的交换,会根据空间局部性原理,去加载更多的数据,比如:16K,24K。这样就可以提高 IO 交换的能力,这是操作系统给我们定义的空间局部性原理。一页的定义,MySQL定义是16K ,MySQL进行磁盘交互,一次性交互16K。操作系统是一次性交互4K

---->通过如上分析,我们发现平衡二叉树,作为MySQL索引 还是差那么点事


3.多路平衡查找树 B-Tree

      B-Tree(多路平衡查找树,并不是二叉的)是一种常见的数据结构。使用 B-Tree 结构可以显著减少定位记录时所经历的中间过程,从而加快存取速度。按照翻译,B 通常认为是Balance的简称。这个数据结构一般用于数据库的索引,综合效率较高。

      路:可理解为"叉"的意思,多路就是多叉,多个分支的意思。

      [关键字与"路"对应关系]:关键字=路数-1
在这里插入图片描述

3.1 B-Tree相比平衡二叉树,优势在哪?

      相比平衡二叉树(共3层能存储7个数据),B-Tree同样是三层,这个存储的数据就很多了,翻倍形式的增长。B-Tree 为多路平衡树,如果我们定成1000路,同样三层 IO 操作,它能检索的数据就更多了。(形象理解:平衡二叉树就是瘦高,B-Tree就是矮胖) 在这种层度上,多路平衡查找树完胜平衡二叉树。

      还记得操作系统是一次性交互 4K 数据,MySQL 一个节点 是 16K。如果我们以 ID 作为索引,一个索引数据(索引数据 4byte + 其他冗余数据4byte(数据区+子类引用等数据))共 8byte (字节)的话,16K 能存放 (16*1024/8=)2048 个索引,那就能存放(2048+1=)2049路。这 2049 路能存放多少数据,就多到数不清了吧。(Tips:加索引时,我们之前只知道数据类型、字段定义越精简越好,为什么呢?因为定义数据类型、字段时,越越精简占用空间越小,一个磁盘块能存放的数据就更多。)

3.2 B-Tree,如何检索数据呢?

   我们以检索 32 为例,流程如下:

  1. 首先将磁盘块1加载到内存中,获取到根节点数据。然后让 3217、35进行对比,发现 17 < 32 < 35。此时会基于 P2 (P1、P2、P3是指向下一个磁盘块的地址),通过顺序 IO 快速的去加载磁盘块 3 中的内容;(Tips:检索数据X:X<17 走P1 17<X<35 走P2 X>35 走P3)
  2. 32 > 29,走P3,加载磁盘块 10 中的内容,然后命中 32。命中后,获取到数据区地址,再根据地址来加载磁盘中的数据。

   这样子,B-Tree 就能够完美解决平衡二叉树存在的缺点了

既然有了B-Tree,那么 MySQL 为什么要采用B+Tree 来作为索引数据结构呢?下面我们就来了解一下B+树

4.加强版多路平衡查找树 B+Tree

在这里插入图片描述
       B+树 是 B树 的变种。B+树,使用的是左闭合形式,删除、添加等基本的机制原理都是类似的。B+树上的叶子结点存储关键字以及相应记录的地址,叶子结点以上各层作为索引使用

4.1 B+树,为什么采用左闭合形式?

  1. 因为B+树中,它认为主键是一个重要的属性。它推崇以 ID 作为索引(即:以数字作为索引)。基本上数字采用的都是自增长,基本就是往右边进行插入。这就是为什么左闭合的一个原因。
  2. B+树的枝节点是不保存数据的,枝节点只保存关键字引用关系,所有的数据都保存在叶子节点
  3. B+树叶子节点是顺序排列的,并且相邻的节点具有顺序引用的关系。

4.2 B+树,如何检索数据呢?

   根据上图,我们以检索 28 为例,流程如下:

  1. 在根节点,我们就发现已经有 28 了。 B树则会直接停止向下检索。B+树则会继续向下检索【因为 B+树枝节点不存储数据,数据都是保存在叶子节点】
  2. 然后他会继续向下去检索,直到走到叶子节点,然后命中 28。命中后,它才会获取到数据区地址,再根据地址来加载磁盘中的数据。

4.3 B+Tree 与 B-Tree 的区别

  1. B+Tree 节点关键字搜索采用闭合区间;
  2. B+Tree 非叶子节点不保存数据相关信息,只保存关键字和子节点引用;
  3. B+Tree 关键字对应的数据保存在叶子节点中
  4. B+叶子节点是顺序排列的,并且相邻节点具有顺序引用的关系

5.MySQL 为什么选择 B+Tree 呢?

  1. B+Tree 是 B-Tree 的变种(Plus版),多路绝对平衡查找树,它拥有 B-Tree 的优势;
  2. B+Tree 扫库、扫表能力更强;
  3. B+Tree 的磁盘读写能力更强;
  4. B+Tree 的排序能力更强;
  5. B+Tree 的查询效率更加稳定。
解析:

1.扫库、扫表能力更强:B-Tree 扫库扫表,会扫每一个枝节点,最后再扫子节点,基本都得扫一遍。B+Tree,只扫子节点即可
2.磁盘读写能力更强B+Tree 枝节点不保存数据,只保存关键字和子节点引用。从理论上来将,能保存的关键字就更多,能加载的关键字就更多。从理论上来讲磁盘的读写能力就更强
3.查询效率更加稳定:查询数据:比如使用 B-Tree 第一次查询比较幸运,第二层就出来了并返回,用了0.2s。B树是平衡树,此时如果又新插入一些数据,导致刚刚查询的数据到达了底层,那么此时第二次查询。时间就要大于0.2s了。即:指点数据节点的深度是会变化的,会随着数据的插入、删除而改变,从而导致查询时间有所不稳定数据更多的话,这个时间就会比第一次差距更大了(B+Tree查询,都会去叶子节点查找数据,相对B-Tree来说就更加稳定)


MySQL 索引底层为什么选择B+Tree,介绍到此为止

如果本文对你有所帮助,那就给我点个赞呗 O(∩_∩)O

End

发布了247 篇原创文章 · 获赞 44 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/lzb348110175/article/details/103999658