为什么MySQL 要选择 B+树

news2024/11/14 21:54:48

一、什么是索引

在关系数据库中,索引是一种单独的、物理的对数据库表中一列或多列的值进行排序的一种存储结构。再直白点就是我们可以把索引理解成图书或者字典的目录。

既然索引是数据的一种存储结构,那么我们必然要对其进行存储,同时,建立索引的目的就是为了加快查询速度,所以必然要选择一种可以高效查询的数据结构来进行存储,而 MySQL 选择了什么数据结构来存储索引呢?

二、二分查找法

在日常开发中,我们经常使用链表或者数组来存储数据,而如果按顺序架构数据存入数组,查找数据时我们就可以可用二分查找法来实现高效查询(注意:因为链表的空间是不连续的,所以不能使用二分查找法)。

二分查找法:Binary Search,也称折半查找法,是一种效率较高的查找方法。比如有 1-10 十个数,现在需要找到 8,先从中间开始找 5,然后发现 8 比 5 大,可以把 5 左边的数排除掉,剩下 6-10,再从中间开始找,依次类推,直到找到 8 为止。

二分查找法有一个前提是数据必须是有序的,存储数据的空间必须是连续的(只有连续的空间才可以通过下标计算出指定数据的位置),这种数据存储一般属于链式存储,我们一但要插入或者修改一个数据,可能会伴随着大量的下标移动,比如我们把 1-10 放在数组里面,下标分别对应 0-9,然后现在要插入一个 0,为了保证有序,0 必须排在第一位,那么 1-10 所有的数据下标都要往后移动一位,这会严重影响到写入性能,所以数组并不适合用来存储索引。

三、二叉树

分查找法是一种效率相对较高的查询方式,但是其有较多的局限性且并不适合于频繁修改的场景,为了解决二分查找的局限和缺陷,有人发明了一种新的数据结构:树。而二叉树又是树中最基本的一种数据结构。

二叉查找树简称二叉树(BST),英文全称:Binary Search Tree,这是一种什么样的数据结构呢?

下图所示就是一棵二叉树:
在这里插入图片描述

在上面这个二叉树中,我们要找到 8,先从根节点 6 开始比较,发现 8 比 6 大,继续往右边的子节点查找,这时就可以找到 8。

二叉树有两个特点:

  • 左子树所有的节点都小于父节点。

  • 右子树所有的节点都大于父节点(这就是上面为什么发现 6 小于当前需要查找节点时需要继续往右边寻找的原因)。

根据上面这棵二叉树,我们可以发现,二叉树的查询效率和这棵树的深度是相关的。因为在实际应用中,每次获取一个节点可能就说明进行了一次磁盘 IO 操作,树越深,需要的 IO 次数可能就会越多,从而查询速度就越慢,在最坏的情况下时间复杂度会退化成 O(n),这时候二叉树就会退化成一个链表,如下图所示:

在这里插入图片描述
当二叉树退化成链表之后,这时候的查询效率就会非常低,只能一个个遍历列表中的元素,直到找到需要的节点。

四、平衡二叉树

二叉树退化成链表之后就表明这棵树只有右子节点或者只有左子节点,也就是说左右节点不平衡,那么这时候就有人想办法采用特定算法让二叉树平衡一点,这就是平衡二叉树。

平衡二叉树,英文全名叫做:Balanced binary search trees,简称 AVL 树,这个 AVL 并不是英文名的简称,而是发明者(G. M. Adelson-Velsky 和 E. M. Landis)两个人的人名缩写。

平衡二叉树的特点就是:左右子树深度差绝对值不能超过 1,一旦超过 1 就会通过特定算法来发生左旋或者右旋操作,以此来保证这颗树的平衡性,避免出现普通二叉树的极端情况下退化成为链表的情况。

有了平衡二叉树,似乎是解决了上面提到的问题,那么我们是不是可以选择平衡二叉树来存储索引呢?然而实际上 MySQL 中的索引并不是采用平衡二叉树来进行存储的,这是为什么呢?

索引需要存储哪些信息

在回答这个问题之前,我们需要了解一下,索引到底需要存储什么?

为了达到数据检索效果,一个索引至少应该包含以下三部分的信息:

  1. 索引值:就是表里面索引列对应的值,因为我们查询就是通过索引值来查询的,所以索引值必然要存储。

  2. 数据的磁盘地址(通过磁盘地址找到当前数据)或者直接存储整条数据:通过索引搜索的目的其实是需要找到当前对应的整条数据,所以必然需要存储地址或者数据

  3. 子节点的地址:任何时候查询都是从根节点开始的,当发现根节点的数据并不是我们想要的数据,我们需要继续往下查询,所以需要知道当前根节点中所有子节点的引用地址。

有了上面的三部分存储信息,我们可以得到下面的一个索引存储简图:
在这里插入图片描述上图中,每个节点中黄色区域就表示索引值,紫红色区域(中间部分)表示当前索引值对应的数据磁盘地址,通过这个地址可以直接获取整条数据,最下面蓝色的左、右存储了子节点的地址。

InnoDB 索引结构

在 InnoDB 存储引擎中,页(Page)是用于管理数据的最小磁盘单位,页的默认大小为 16KB(不同版本会有差异)。而这个页也就是对应了上图中的每一个节点,每查询一次节点就需要进行一次 IO 操作。

那么问题就来了,上图中,AVL 树一个节点上只存了一个关键字(索引值)+ 一个磁盘地址+ 左右节点的引用,这几个信息加起来占用了多大空间,我们可以来算一下:

  • 索引关键字:假设是采用 32 位的 uuid 进行存储,那么就是占了 32 个字节。

  • 数据磁盘地址:这一块其实在 InnoDB 中采用的是 8 个字节进行存储。

  • 子节点的引用地址:假设有 2 个子节点,那么这一块也是占用了 8 * 2 字节。

上面三部分内容加起来总共是 56 个字节,而 16kb 有多少个字节呢?答案是 16384 个字节。

所以如果采用平衡二叉树来存储索引的话,我们一个节点存储的内容是远远小于 16kb,这样看来,一个节点只存储一个关键字,浪费了大量的空间。

而且因为二叉树只有两路,数据量一上来,整颗树就会变得非常深,这也会很影响查询性能,所以我们需要做的就是将一棵“瘦高”的树变成“矮胖”的树。

将一棵“瘦高”的树变成“矮胖”的树最简单的办法就是将路数变多,也就是让一个节点存储更多的关键字。

五、红黑树

与AVL树相比,红黑树并不追求严格的平衡,而是大致的平衡。但红黑树的查询效率会有所下降,这是因为树的平衡性变差,高度更高。但红黑树的删除效率大大提高了,因为红黑树同时引入了颜色,当插入或删除数据时,只需要进行O(1)次数的旋转以及变色就能保证基本的平衡,不需要像AVL树进行O(lgn)次数的旋转。

因此,在实际应用中,AVL树的使用相对较少,而红黑树的使用非常广泛。例如,Java中的TreeMap使用红黑树存储排序键值对;Java8中的HashMap使用链表+红黑树解决哈希冲突问题(当冲突节点较少时,使用链表,当冲突节点较多时,使用红黑树)。

对于数据在内存中的情况(如上述的TreeMap和HashMap),红黑树的表现是非常优异的。但是对于数据在磁盘等辅助存储设备中的情况(如MySQL等数据库),红黑树并不擅长,因为红黑树长得还是太高了。当数据在磁盘中时,磁盘IO会成为最大的性能瓶颈,设计的目标应该是尽量减少IO次数;而树的高度越高,增删改查所需要的IO次数也越多,会严重影响性能。

六、多路平衡树(B 树)

B 树有一个特点就是:分叉数(路数)永远比关键字数多 1。

如下图就是一棵简易的 B 树示意图(白色框表示路数,蓝色框表示索引值)
在这里插入图片描述可以看到这棵树的中间一层,存储了两个关键字,所以其下一层就有了三个子节点,所以关键字存储越多,路数越多,同样深度的树就能存储更多的数据。

多路平衡树解决了二叉树和平衡二叉树的问题,目前看起来性能应该不错,但是 MySQL 依然没有选择 B 树来存储索引,原因我们后面解释,我们先来看看 B 树的升级版本:B+ 树。

七、B+ 树

下图是一棵 B+ 树的存储数据示意图:
在这里插入图片描述

对比 B 树,其实 B+ 树有一个非常明显的特点,那就是最后一层的叶子节点会有指向下一个节点的指针,从而形成了一个有序链表。

在 InnoDB 中,B+ 树有以下特点:

  • B+ 树的关键字的数量是跟路数相等的。

  • B+ 树的根节点和枝节点中都不会存储数据,只有叶子节点才存储数据。而搜索到关键字也不会直接返回,也仍然会到最后一层的叶子节点。

  • B+ 树的每个叶子节点增加了一个指向相邻叶子节点的指针,它的最后一个数据会指向下一个叶子节点的第一个数据,形成了一个有序链表的结构。

八、为什么要选择 B+ 树

B+ 树本身是由 B 树改进而来的,所以 B 树能解决的问题,B+ 树都能解决,而且 B+ 树相比较 B 树更有以下优势:

  • 扫库、扫表能力更强:如果我们要对表进行全表扫描,只需要遍历叶子节点就可以了,不需要遍历整棵 B+ 树,因为其数据只存储在叶子节点。

  • B+ 树的磁盘读写能力相对于 B 树来说更强: B+ 树的根节点和枝节点不保存数据,所以一个节点可以保存更多的关键字,一次磁盘加载(即一次 IO 操作)能获取到相对更多的关键字。

  • 天然具备排序能力:叶子节点上有下一个数据区的指针,数据形成了链表。

  • 效率稳定:B+ 树永远是在叶子节点拿到数据,所以 IO 次数是稳定的,而 B树运气好根节点就拿到数据,运气不好就要到叶子节点才能拿到数据,所花费的时间会有差异。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/137309.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

你可以不用Git,但不能不会Git(二)基础(上)

目录 一.环境配置 二.本地初始化仓库 三.文件的两种状态 四.文件加入暂存区 五.文件提交与删除 1.提交文件 2.删除文件 一.环境配置 当安装Git后首先要做的事情是设置用户名称和email地址。这是非 常重要的,因为每次Git提交都会使用该用户信息。 设置用户信…

#G. 红旗招展

Description小J占山为王有一段时间了,他决定把自己的地盘打扮一下---将各种颜色的旗插在山头上当然某一座的山头只能用一种颜色的旗。整个地盘有N个山头,可看成是一棵有N个节点的树。小J还是有一点艺术细胞的,他不想整个地盘杂乱无章。于是他…

Kubernetes安装ingress-controller

IngressController 1. 安装 1.1 查看ingress-nginx的版本匹配 https://github.com/kubernetes/ingress-nginx/blob/main/README.md#readme 下载配置 wget https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.1.2/deploy/static/provider/cloud/de…

【回答问题】ChatGPT上线了!给我推荐20个比较流行的图像实例分割模型

给我推荐20个比较流行的图像实例分割模型 U-Net Mask R-CNN FCN SegNet PSPNet DeepLabv3 DenseASPP LinkNet RefineNet ENet ICNet BiSeNet CRF-RNN GCN Tiramisu FRRN DeepLabv3 PANet MaskLab Hybrid Task Cascade给我推荐20个比较流行的图像实例分割模型源码 好的&#x…

java学习之类变量

目录 一、类变量的引出 需解决的问题 传统代码 代码问题 解决方法 运行结果 二、类变量的内存布局 特点 内存中的位置 三、类变量定义 四、类变量语法 五、访问类变量 六、类变量细节 第一条 第二条 第三条 第四条 第五条 第六条 第七条 一、类变量的引出 需…

PHP8---MySQL扩展

目录 一、MySQL扩展的介绍 二、PHP和MySQL连接的基本操作 (1)MySQL连接的准备工作 (2)插入删除操作 三、查询操作 (1)mysqli_fetch_assoc (2)mysqli_fetch_row (3…

19. 网站响应数据加一个简单的密,就能挡住80%的爬虫,你信吗?

本篇博客我们实现响应加密,由于本案例是JS逆向阶段的第一个案例,所以采用最基础加密手段。 爬虫训练场源码同步仓库为 GitCode 项目采集测试地址:爬虫训练场 爬虫训练场框架搭建Python Flask 端 Base64加密前台解密字符串渲染数据框架搭建 本…

Es之mapping

1)、字段类型 2)、映射 Mapping(映射) Mapping 是用来定义一个文档(document),以及它所包含的属性(field)是如何存储和 索引的。比如,使用 mapping 来定义…

深入解析Linux虚拟化KVM-Qemu分析之KVM源码

说明: KVM版本:5.9.1QEMU版本:5.0.0工具:Source Insight 3.5, Visio 1. 概述 从本文开始将开始source code的系列分析了;KVM作为内核模块,可以认为是一个中间层,向上对接用户的控制…

idea中热部署插件JRebel的激活方式

idea中热部署插件JRebel的激活方式 一、打开jrebel 激活面板,如图: 二、选中Team URL(connect to online licensing service) 1、在上面的框中输入激活的url地址 http://127.0.0.1:8888/132d042c-3b1a-4c45-9044-b7897c3de7882…

遗传算法改进(IGA)+python代码实现

遗传算法改进(IGA)python代码实现一、变异概率的改进(1)单点变异(2)多点变异(3)选择性的突变概率二、交叉概率的改进三、适应度函数的改进(1)sigmoid函数&…

PGL 系列(六)node2vec

node2vec DeepWalk存在的问题是比较简单直接,而图结构往往是一个复杂结构,需要考虑很多因素,在深度优先搜索方法之外,还有广度优先搜索,结合以上两种方式可以更好的探索图模型,即node2vec。

华为交换机配置

文章目录网络规划与设计机房连线图PON网络配置网络规划与设计 OLT: 网络光线路终端(Optical Line Terminal) ONU分为两种 MDU(Multi-DwellingUnit,多住户单元) MDU主要应用于FTTB应用类型下的多个住宅用户的接入,一般具有至少4个…

【JDK工具】jinfo、jps、jstack、jstat、jmap

目录一、前言二、关键工具2.1 jps 显示所有JAVA进程信息1. 参数信息2. 常用命令2.2 jinfo 查看虚拟机配置参数信息1. 查看虚拟机参数 jinfo -flags pid2. 查看虚拟机指定参数 jinfo -flag 具体参数 pid3. 查看环境变量 jinfo -sysprops pid4. 参数列表2.3 jstack1. 能排查哪些问…

springboot整合之Validated参数校验

特别说明:本次项目整合基于idea进行的,如果使用Eclipse可能操作会略有不同,不过总的来说不影响。 springboot整合之如何选择版本及项目搭建 springboot整合之版本号统一管理 springboot整合mybatis-plusdurid数据库连接池 springboot整…

JVM面试大总结

一、汇总 JVM是运行在操作系统之上的,它与硬件没有直接的交互。先说一下JVM的内存区域,当函数开始运行时,JVM拿到自己的内存将自己的内存区域进行了分割,分为五块区域:线程共享的有堆、方法区,线程私有的有…

Hadoop MapReduce 介绍

Hadoop MapReduceMapReduce核心思想设计构思什么是MapReduceMapReduce的特点MapReduce的不足(局限性)MapReduce组成Hadoop MapReduce实现流程map阶段执行过程Reduce阶段执行过程Shuffle机制Map端ShuffleReducer端的shuffleMapReduce核心思想 MapReduce的…

基于MVC的在线购物系统

摘 要本毕业设计的内容是设计并且实现一个基于net语言的在线购物系统。它是在Windows下,以SQL Server为数据库开发平台,Tomcat网络信息服务作为应用服务器。在线购物系统的功能已基本实现,主要包括首页、个人中心、会员用户管理、商品分类管理…

Mac操作系统配置Git

下载Git mac在安装git时,一般只需要一行指令brew install git 验证Git 在我们安装过Git之后,我们可以输入git --version进行验证。如果我们成功进行了安装之后,我们可以看到下图这样的结果。 配置Gitee 生成密钥对 首先我们通过cd ~/.…

Allegro174版本新功能介绍之Symphony模式下放置器件

Allegro174版本新功能介绍之Symphony模式下放置器件 Allegro在172版本在Symphony模式下,是无法放置器件的,如下图 只有一个Swap的选项 在升级到了174版本的时候,Symphony模式是同样支持放器件的 具体介绍如下 任意打开一个174版本的PCB,选择Start Symphony Server