搜索
首页数据库mysql教程终于理解 MySQL 索引要用 B+tree ,而且还这么快

mysql教程栏目介绍理解索引的B+tree。

终于理解 MySQL 索引要用 B+tree ,而且还这么快

免费推荐:mysql教程(视频)

前言

当你现在遇到了一条慢 SQL 需要进行优化时,你第一时间能想到的优化手段是什么?

大部分人第一反应可能都是添加索引,在大多数情况下面,索引能够将一条 SQL 语句的查询效率提高几个数量级

索引的本质:用于快速查找记录的一种数据结构

索引的常用数据结构

  1. 二叉树
  2. 红黑树
  3. Hash 表
  4. B-tree (B树,并不叫什么B减树)
  5. B+tree

数据结构图形化网址:https://www.cs.usfca.edu/~galles/visualization/Algorithms.html

索引查询

大家知道 select * from t where col = 88 这么一条 SQL 语句如果不走索引进行查找的话,正常地查就是全表扫描:从表的第一行记录开始逐行找,把每一行的 col 字段的值和 88 进行对比,这明显效率是很低的。

终于理解 MySQL 索引要用 B+tree ,而且还这么快

而如果走索引的话,查询的流程就完全不一样了(假设现在用一棵平衡二叉树数据结构存储我们的索引列)

此时该二叉树的存储结构(Key - Value):Key 就是索引字段的数据,Value 就是索引所在行的磁盘文件地址。

当最后找到了 88 的时候,就可以把它的 Value 对应的磁盘文件地址拿出来,然后就直接去磁盘上去找这一行的数据,这时候的速度就会比全表扫描要快很多。

终于理解 MySQL 索引要用 B+tree ,而且还这么快

实际上 MySQL 底层并没有用二叉树来存储索引数据,是用的 B+tree(B+树)

为什么不采用二叉树

假设此时用普通二叉树记录 id 索引列,我们在每插入一行记录的同时还要维护二叉树索引字段。

终于理解 MySQL 索引要用 B+tree ,而且还这么快

此时当我要找 id = 7 的那条数据时,它的查找过程如下:

终于理解 MySQL 索引要用 B+tree ,而且还这么快

此时找 id = 7 这一行记录时找了 7 次,和我们全表扫描也没什么很大区别。显而易见,二叉树对于这种依次递增的数据列其实是不适合作为索引的数据结构。

为什么不采用 Hash 表

Hash 表:一个快速搜索的数据结构,搜索的时间复杂度 O(1)

Hash 函数:将一个任意类型的 key,可以转换成一个 int 类型的下标

假设此时用 Hash 表记录 id 索引列,我们在每插入一行记录的同时还要维护 Hash 表索引字段。

终于理解 MySQL 索引要用 B+tree ,而且还这么快

这时候开始查找 id = 7 的树节点仅找了 1 次,效率非常高了。

终于理解 MySQL 索引要用 B+tree ,而且还这么快

MySQL 的索引依然不采用能够精准定位的Hash 表。因为它不适用范围查询

为什么不采用红黑树

红黑树是一种特化的 AVL树(平衡二叉树),都是在进行插入和删除操作时通过特定操作保持二叉查找树的平衡;

若一棵二叉查找树是红黑树,则它的任一子树必为红黑树。

假设此时用红黑树记录 id 索引列,我们在每插入一行记录的同时还要维护红黑树索引字段。

终于理解 MySQL 索引要用 B+tree ,而且还这么快

插入过程中会发现它与普通二叉树不同的是当一棵树的左右子树高度差 > 1 时,它会进行自旋操作,保持树的平衡。

这时候开始查找 id = 7 的树节点只找了 3 次,比所谓的普通二叉树还是要更快的。

终于理解 MySQL 索引要用 B+tree ,而且还这么快

MySQL 的索引依然不采用能够精确定位和范围查询都优秀的红黑树

因为当 MySQL 数据量很大的时候,索引的体积也会很大,可能内存放不下,所以需要从磁盘上进行相关读写,如果树的层级太高,则读写磁盘的次数(I/O交互)就会越多,性能就会越差。

B-tree

红黑树目前的唯一不足点就是树的高度不可控,所以现在我们的切入点就是树的高度

目前一个节点是只分配了一个存储 1 个元素,如果要控制高度,我们就可以把一个节点分配的空间更大一点,让它横向存储多个元素,这个时候高度就可控了。这么个改造过程,就变成了 B-tree

B-tree 是一颗绝对平衡的多路树。它的结构中还有两个概念

度(Degree):一个节点拥有的子节点(子树)的数量。(有的地方是以来说明 B-tree 的,这里解释一下)

阶(order):一个节点的子节点的最大个数。(通常用 m 表示)

关键字:数据索引。

一棵 m 阶 B-tree 是一棵平衡的 m 路搜索树。它可能是空树,或者满足以下特点:

  1. 除根节点和叶子节点外,其它每个节点至少有 m2\lceil \dfrac{m}{2}\rceil 个子节点;

    m2\lceil \dfrac{m}{2}\rceil 为 m / 2 然后向上取整

  2. 每个非根节点所包含的关键字个数 j 满足:m2\lceil \dfrac{m}{2}\rceil - 1 ≤ j ≤ m - 1;

  3. 节点的关键字从左到右递增排列,有 k 个关键字的非叶子节点正好有 (k + 1) 个子节点;

  4. 所有的叶子结点都位于同一层。

名字取义(题外话,放松一下)

以下摘自维基百科

鲁道夫·拜尔(Rudolf Bayer)和 艾华·M·麦克雷(Ed M. McCreight)于1972年在波音研究实验室(Boeing Research Labs)工作时发明了 B-tree,但是他们没有解释 B 代表什么意义(如果有的话)。

道格拉斯·科默尔(Douglas Comer)解释说:两位作者从来都没解释过 B-tree 的原始意义。我们可能觉得 balanced, broad 或 bushy 可能适合。其他人建议字母 B 代表 Boeing。源自于他的赞助,不过,看起来把 B-tree 当作 Bayer 树更合适些。

高德纳(Donald Knuth)在他1980年5月发表的题为 "CS144C classroom lecture about disk storage and B-trees" 的论文中推测了 B-tree 的名字取义,提出 B 可能意味 Boeing 或者 Bayer 的名字。

查找

B-tree 的查找其实和二叉树很相似:

二叉树是每个节点上有一个关键字和两个分支,B-tree 上每个节点有 k 个关键字和 (k + 1) 个分支。

二叉树的查找只考虑向左还是向右走,而 B-tree 中需要由多个分支决定。

B-tree 的查找分两步:

  1. 首先查找节点,由于 B-tree 通常是在磁盘上存储的所以这步需要进行磁盘IO操作;
  2. 查找关键字,当找到某个节点后将该节点读入内存中然后通过顺序或者折半查找来查找关键字。若没有找到关键字,则需要判断大小来找到合适的分支继续查找。

操作流程

现在需要查找元素:88

第一次:磁盘IO

第二次:磁盘IO

第三次:磁盘IO

然后这有一次内存比对,分别跟 70 与 88 比对,最后找到 88。

从查找过程中发现,B-tree 比对次数和磁盘IO的次数其实和二叉树相差不了多少,这么看来并没有什么优势。

但是仔细一看会发现,比对是在内存中完成中,不涉及到磁盘IO,耗时可以忽略不计。

另外 B-tree 中一个节点中可以存放很多的关键字(个数由阶决定),相同数量的关键字B-tree 中生成的节点要远远少于二叉树中的节点,相差的节点数量就等同于磁盘IO的次数。这样到达一定数量后,性能的差异就显现出来了。

插入

B-tree 要进行插入关键字时,都是直接找到叶子节点进行操作。

  1. 根据要插入的关键字查找到待插入的叶子节点;
  2. 因为一个节点的子节点的最大个数(阶)为 m,所以需要判断当前节点关键字的个数是否小于 (m - 1)。
    • 是:直接插入
    • 否:发生节点分裂,以节点的中间的关键字将该节点分为左右两部分,中间的关键字放到父节点中即可。

操作流程

比如我们现在需要在 Max Degree(阶)为 3 的 B-tree 插入元素:72

  1. 查找待插入的叶子节点

  2. 节点分裂:本来应该和 [70,88] 在同一个磁盘块上,但是当一个节点有 3 个关键字的时候,它就有可能有 4 个子节点,就超过了我们所定义限制的最大度数 3,所以此时必须进行分裂:以中间关键字为界将节点一分为二,产生一个新节点,并把中间关键字上移到父节点中。

Tip : 当中间关键字有两个时,通常将左关键字进行上移分裂。

删除

删除操作就会比查找和插入要麻烦一些,因为要被删除的关键字可能在叶子节点上,也可能不在,而且删除后还可能导致 B-tree 的不平衡,又要进行合并、旋转等操作去保持整棵树的平衡。

随便拿棵树(5 阶)举例子

以上是终于理解 MySQL 索引要用 B+tree ,而且还这么快的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文转载于:juejin。如有侵权,请联系admin@php.cn删除
图文详解mysql架构原理图文详解mysql架构原理May 17, 2022 pm 05:54 PM

本篇文章给大家带来了关于mysql的相关知识,其中主要介绍了关于架构原理的相关内容,MySQL Server架构自顶向下大致可以分网络连接层、服务层、存储引擎层和系统文件层,下面一起来看一下,希望对大家有帮助。

mysql的msi与zip版本有什么区别mysql的msi与zip版本有什么区别May 16, 2022 pm 04:33 PM

mysql的msi与zip版本的区别:1、zip包含的安装程序是一种主动安装,而msi包含的是被installer所用的安装文件以提交请求的方式安装;2、zip是一种数据压缩和文档存储的文件格式,msi是微软格式的安装包。

mysql怎么去掉第一个字符mysql怎么去掉第一个字符May 19, 2022 am 10:21 AM

方法:1、利用right函数,语法为“update 表名 set 指定字段 = right(指定字段, length(指定字段)-1)...”;2、利用substring函数,语法为“select substring(指定字段,2)..”。

mysql怎么替换换行符mysql怎么替换换行符Apr 18, 2022 pm 03:14 PM

在mysql中,可以利用char()和REPLACE()函数来替换换行符;REPLACE()函数可以用新字符串替换列中的换行符,而换行符可使用“char(13)”来表示,语法为“replace(字段名,char(13),'新字符串') ”。

mysql怎么将varchar转换为int类型mysql怎么将varchar转换为int类型May 12, 2022 pm 04:51 PM

转换方法:1、利用cast函数,语法“select * from 表名 order by cast(字段名 as SIGNED)”;2、利用“select * from 表名 order by CONVERT(字段名,SIGNED)”语句。

MySQL复制技术之异步复制和半同步复制MySQL复制技术之异步复制和半同步复制Apr 25, 2022 pm 07:21 PM

本篇文章给大家带来了关于mysql的相关知识,其中主要介绍了关于MySQL复制技术的相关问题,包括了异步复制、半同步复制等等内容,下面一起来看一下,希望对大家有帮助。

mysql怎么判断是否是数字类型mysql怎么判断是否是数字类型May 16, 2022 am 10:09 AM

在mysql中,可以利用REGEXP运算符判断数据是否是数字类型,语法为“String REGEXP '[^0-9.]'”;该运算符是正则表达式的缩写,若数据字符中含有数字时,返回的结果是true,反之返回的结果是false。

mysql怎么删除unique keymysql怎么删除unique keyMay 12, 2022 pm 03:01 PM

在mysql中,可利用“ALTER TABLE 表名 DROP INDEX unique key名”语句来删除unique key;ALTER TABLE语句用于对数据进行添加、删除或修改操作,DROP INDEX语句用于表示删除约束操作。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前By尊渡假赌尊渡假赌尊渡假赌

热工具

VSCode Windows 64位 下载

VSCode Windows 64位 下载

微软推出的免费、功能强大的一款IDE编辑器

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

螳螂BT

螳螂BT

Mantis是一个易于部署的基于Web的缺陷跟踪工具,用于帮助产品缺陷跟踪。它需要PHP、MySQL和一个Web服务器。请查看我们的演示和托管服务。

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

适用于 Eclipse 的 SAP NetWeaver 服务器适配器

将Eclipse与SAP NetWeaver应用服务器集成。