深入InnoDB核心:揭秘B+树在数据库索引中的高效应用

news2024/9/23 0:41:40

目录

一、索引页与数据行的紧密关联

(一)数据页的双向链表结构

(二)记录行的单向链表结构

二、未创建索引情况

(一)无索引下的单页查找过程

以主键为搜索条件

以非主键列为搜索条件

(二)无索引下的多页查找过程

三、InnoDB中的B+ 树索引方案初体会

(一)前置说明

行格式示意图

页内格式示意图

(二)目录项记录与用户记录的区别及其存储

目录项放到数据页

查找主键为20的记录

(三)分配新目录项记录页

更新后的数据页结构

查找主键值为20的记录的步骤

确定目录项记录页

通过目录项记录页确定用户记录所在的页

在用户记录页中定位具体记录

(四)多级目录项记录页的介绍

高级目录页说明

高级目录页(页33)

页30(目录项记录页)

页32(目录项记录页)

查找主键值为20的记录的步骤(更新后的过程)

四、InnoDB中的B+ 树索引方案确立

(一)B+树的数据页结构

(二)B+树的层次结构

(三)B+树的查找效率

五、总结

 参考文献、书籍及链接


干货分享,感谢您的阅读!

在现代数据库系统中,如何高效地管理和查找海量数据是一个至关重要的问题。InnoDB作为MySQL的存储引擎,通过引入B+树这种数据结构,解决了这一挑战。B+树不仅在索引性能方面表现优异,还在数据存储和检索上提供了极高的效率。我们探讨下B+树在InnoDB中的实现和应用,更好地理解其工作原理和优势。

一、索引页与数据行的紧密关联

在解读InnoDB数据库索引页与数据行的紧密关联-CSDN博客中我们已经强调过:在 InnoDB 中,数据页通过双向链表连接,每个数据页内的记录行按照主键值从小到大的顺序组成单向链表,并且每个数据页都有一个页目录用于快速定位记录。

查找记录时,先在页目录中使用二分法定位到特定槽,再在该槽对应的记录组中顺序遍历找到目标记录。通过这种设计,InnoDB 能够高效地管理和查找数据,确保数据库系统的高性能和可靠性。

(一)数据页的双向链表结构

每个数据页被组织成一个双向链表,这意味着每个数据页都有指向前一个页和后一个页的指针(File Header 记录了页的基础信息和链表指针)。通过这种双向链表结构,InnoDB 可以方便地进行数据页的插入、删除和遍历操作。这种设计保证了数据页之间的高效连接和管理。

(二)记录行的单向链表结构

 具体可见:数据库记录行在页内查询探索分析_检查代码中循环依赖-CSDN博客

在每个数据页中,记录行按照主键值从小到大的顺序组织成一个单向链表。这种有序的结构使得在数据页内查找记录变得更加高效。每条记录不仅存储了自身的数据,还包含指向下一条记录的指针,这样可以顺序遍历记录。

每个数据页都有一个页目录,页目录可以看作是数据页内的索引结构。页目录将记录分成多个组,每个组在页目录中都有一个槽。通过页目录,InnoDB 可以快速定位到特定记录所在的组,从而减少遍历记录的时间。

当需要通过主键查找某条记录时,InnoDB 会先在页目录中使用二分法快速定位到对应的槽。页目录中的槽指向该槽对应的记录组,接着在该组中遍历记录,直到找到目标记录。这种查找过程结合了二分查找和顺序遍历的优点,既高效又精确。

二、未创建索引情况

在数据库中,当没有针对特定列建立索引时,查询过程通常会面临效率低下的问题。特别是对于非主键列的精确匹配查询,数据库引擎无法利用索引结构,从而导致需要全表扫描来寻找符合条件的记录。

(一)无索引下的单页查找过程

在数据库中,当所有记录都能够存放在一个数据页中时,查找记录的过程可以根据查询条件的不同分为以下两种情况:

以主键为搜索条件

对于主键列的精确匹配查询:

  1. 页目录查找:数据页通常会维护一个页目录,用于存储每条记录的偏移位置。由于页目录是按照主键排序的,因此可以使用二分查找法快速定位到对应的记录槽。
  2. 记录遍历:找到对应槽后,只需遍历该槽中的记录,即可快速找到符合条件的记录。

这种方法由于利用了页目录的有序性,因此查找效率较高。这部分其实就是我们上文中讲到的理想情况,但在开发过程中我们不可能每次都是直接查主键id的情况。

以非主键列为搜索条件

对于非主键列的精确匹配查询:

  1. 全页扫描:由于非主键列没有对应的页目录,也没有任何排序信息,无法使用二分查找等高效查找算法。此时,数据库需要从数据页的最小记录开始,逐条遍历每条记录。
  2. 逐条比较:在遍历过程中,逐条对比记录是否符合搜索条件。如果记录数较多,这种方法的查找效率将会非常低。

显然,对于非主键列的查找,由于缺乏索引的支持,只能进行全页扫描,这在数据量较大时,性能会变得极为低下。

(二)无索引下的多页查找过程

在大部分实际应用中,表中的记录数量非常庞大,往往需要多个数据页来存储这些记录。当在多页中查找记录时,查找过程通常可以分为两个步骤:

  1. 定位到记录所在的页
  2. 从所在的页中查找相应的记录

在没有索引的情况下,无论是根据主键列还是其他列的值进行查找,由于无法快速定位到记录所在的页,只能沿着数据页的双向链表逐页查找。

这意味着:

  1. 遍历页链表:从第一个数据页开始,沿着双向链表依次访问每一个数据页。
  2. 逐页查找:在每个数据页中,使用前面介绍的单页查找方式来查找指定的记录。

由于需要遍历所有数据页并在每个页中逐条比较记录,这种方式的效率非常低。

三、InnoDB中的B+ 树索引方案初体会

(一)前置说明

为了更好地理解 InnoDB 中 B+ 树索引的工作机制,我们从创建一个示例表index_demo开始,并通过详细的示意图展示记录在页中的存储结构及索引的作用。

CREATE TABLE index_demo (
    c1 INT,
    c2 INT,
    c3 CHAR(1),
    PRIMARY KEY (c1)
) ROW_FORMAT = Compact;

这个表中有两个 INT 类型的列 c1c2,一个 CHAR(1) 类型的列 c3,并且 c1 列为主键。表的行格式为 Compact。

行格式示意图

为了更好地展示记录在页中的存储,简化 index_demo 表的行格式示意图:

每条记录由以下部分组成:

(背景:解析MYSQL行头信息数据详细和探究InnoDB Compact行格式背后)

  1. record_type:记录头信息的一项属性,表示记录的类型:0 表示普通记录;1 表示目录项记录;2 表示最小记录;3 表示最大记录
  2. next_record:记录头信息的一项属性,表示下一条记录地址相对于本记录的地址偏移量。
  3. 各个列的值:记录 index_demo 表中的三个列 c1c2c3 的值。
  4. 其他信息:除了上述三种信息以外的所有信息,包括其他隐藏列的值以及记录的额外信息。

为了节省篇幅,我们省略记录的其他信息部分并将记录竖着展示:

页内格式示意图

把一些记录放到页里边的示意图就是:

(二)目录项记录与用户记录的区别及其存储

在 InnoDB 中,为了灵活管理 B+ 树索引的目录项,设计者们选择了复用存储用户记录的数据页来存储目录项记录。这种设计使得 InnoDB 能够高效地管理和查询数据。我们来详细分析并讲解这一机制。

目录项放到数据页

有效地利用已有的数据结构和存储机制,通过记录头信息中的 record_type 属性,InnoDB 可以轻松区分目录项记录和用户记录。record_type 值为 1 表示目录项记录,值为 0 表示普通用户记录。

目录项记录仅包含主键值和页号,这使得它们非常简洁高效。而普通用户记录则包含用户定义的多个列。

min_rec_mask 属性帮助 InnoDB 快速定位页中主键值最小的目录项记录,优化了索引的管理和查询性能。在存储目录项记录的页中,主键值最小的目录项记录的 min_rec_mask 值为 1,其他记录的 min_rec_mask 值为 0

查找主键为20的记录

现在以查找主键为20的记录为例,根据某个主键值去查找记录的步骤就可以大致拆分成下面两步:

  1. 先到存储目录项记录的页,也就是页30中通过二分法快速定位到对应目录项,因为12 < 20 < 209,所以定位到对应的记录所在的页就是页9

  2. 再到存储用户记录的页9中根据二分法快速定位到主键值为20的用户记录。

(三)分配新目录项记录页

尽管目录项记录只存储主键值和对应的页号,相比于用户记录所需的存储空间要小得多,但由于每个数据页只有16KB的大小,能够存放的目录项记录数量也是有限的。如果表中的数据非常多,以至于单个数据页无法存放所有的目录项记录,那么就需要分配更多的目录项记录页。

假设一个存储目录项记录的页最多只能存放4条目录项记录(请注意这只是个假设,实际情况可以存放更多)。当需要插入新的目录项记录时,比如主键值为320的记录,就会出现需要分配新目录项记录页的情况。

插入一条主键值为320的用户记录后,我们需要新增两个数据页:

  1. 页31:用于存储新的用户记录。
  2. 页32:用于存储新的目录项记录,因为原先存储目录项记录的页30已经满了(假设只能存储4条目录项记录)。

更新后的数据页结构

  • 页30:存储目录项记录,主键值范围为 [1, 320)。
  • 页32:存储目录项记录,主键值范围为 [320, ∞)。

查找主键值为20的记录的步骤

现在因为存储目录项记录的页不止一个,所以如果我们想根据主键值查找一条用户记录,大致需要三个步骤。以查找主键值为20的记录为例:

确定目录项记录页
  • 现在的存储目录项记录的页有两个:页30和页32。
  • 页30存储的目录项的主键值范围是 [1, 320),页32存储的目录项的主键值范围是不小于320。
  • 因此,主键值为20的记录对应的目录项记录在页30中。
通过目录项记录页确定用户记录所在的页

在页30中,通过二分法快速定位到主键值为20的目录项记录,从而确定该用户记录存储在页9中。

在用户记录页中定位具体记录

在页9中,通过二分法快速定位到主键值为20的用户记录。

(四)多级目录项记录页的介绍

在查询步骤的第1步中,我们需要定位存储目录项记录的页。然而,这些页在存储空间中可能不连续排列。如果表中的数据量非常大,会产生许多存储目录项记录的页。那我们怎么根据主键值快速定位一个存储目录项记录的页呢?

为了解决这个问题,我们可以为这些存储目录项记录的页生成一个更高级的目录,形成多级目录结构。大目录中嵌套小目录,小目录中存储实际的数据。现在各个页的示意图如下:

高级目录页说明

高级目录页(页33)

页33存储更高级的目录项记录,用于指向存储目录项记录的页(例如页30和页32)。页33包含两个目录项记录:

  • 目录项记录1:指向页30,范围为[1, 320)
  • 目录项记录2:指向页32,范围为[320, ∞)
页30(目录项记录页)
  • 页30存储具体的目录项记录,指向用户记录页。
  • 目录项记录指向主键值范围在[1, 320)内的用户记录页。
页32(目录项记录页)
  • 页32存储具体的目录项记录,指向用户记录页。
  • 目录项记录指向主键值范围在[320, ∞)内的用户记录页。

查找主键值为20的记录的步骤(更新后的过程)

  • 确定高级目录页:首先,根据主键值在高级目录页(页33)中进行查找。由于主键值20在范围[1, 320)内,所以对应的目录项记录在页30中。
  • 确定具体的目录项记录页:在页30中,通过二分法快速定位到主键值为20的目录项记录,从而确定用户记录存储在页9中。
  • 在用户记录页中定位具体记录:在页9中,通过二分法快速定位到主键值为20的用户记录。

这里注意我们之前的假设:假设一个存储目录项记录的页最多只能存放4条目录项记录(请注意这只是个假设,实际情况可以存放更多)

四、InnoDB中的B+ 树索引方案确立

在介绍B+树之前,我们已经看到了如何通过多级目录结构管理和查找数据。这种结构就像一棵倒过来的树,上头是树根,下头是树叶。其实,这是一种组织数据的形式,或者说是一种数据结构,它的名称是B+树。

(一)B+树的数据页结构

不论是存放用户记录的数据页,还是存放目录项记录的数据页,我们都把它们存放到B+树这个数据结构中,所以我们也称这些数据页为节点。

从图中可以看出来,我们的实际用户记录都存放在B+树的最底层的节点上,这些节点也被称为叶子节点或叶节点,其余用来存放目录项的节点称为非叶子节点或者内节点,其中B+树最上面的那个节点也称为根节点。

(二)B+树的层次结构

设计InnoDB的大佬们为了讨论方便,规定最下面的那层,也就是存放我们用户记录的那层为第0层,之后依次往上加。为了简化,我们之前做了一个非常极端的假设:存放用户记录的页最多存放3条记录,存放目录项记录的页最多存放4条记录。其实在真实环境中,一个页存放的记录数量是非常大的。

假设所有存放用户记录的叶子节点代表的数据页可以存放100条用户记录,所有存放目录项记录的内节点代表的数据页可以存放1000条目录项记录,那么:

  • 如果B+树只有1层,也就是只有1个用于存放用户记录的节点,最多能存放100条记录。
  • 如果B+树有2层,最多能存放1000×100=100,000条记录。
  • 如果B+树有3层,最多能存放1000×1000×100=100,000,000条记录。
  • 如果B+树有4层,最多能存放1000×1000×1000×100=100,000,000,000条记录。

哇咔咔~这么多的记录!!!

但实际上,可推演得出结论:当单行数据大小为S字节,树高为H时,一个bigint类型的主键B+树索引中可以存放的数量行数N等于:

按公式计算:当树高为4时,可以存放200百多亿行数据。这样的数据容量,可以满足绝大部分应用的需求,因此我们可以说在绝大部分应用中,B+树高度为3或4就可以满足数据存储的需求。B+树这种高扇出低树高的特征,也大大的提高了主键查询性能。具体推导可见:InnoDB存储引擎B+树的树高推导_b+树一般多少层-CSDN博客

(三)B+树的查找效率

你的表里能存放100,000,000,000条记录么?所以一般情况下,我们用到的B+树都不会超过4层,那我们通过主键值去查找某条记录最多只需要做4个页面内的查找(查找3个目录项页和一个用户记录页),又因为在每个页面内有所谓的Page Directory(页目录),所以在页面内也可以通过二分法实现快速定位记录,这不是很牛么!

通过引入B+树,InnoDB能够高效地管理和查找大量数据。B+树的结构保证了即使在处理大规模数据时,查找过程仍然高效。每一层增加的节点数,使得数据的管理和查找变得更加迅速和可靠。

B+树是数据库系统中广泛使用的索引结构,通过这种结构,InnoDB能够在庞大的数据集中快速、准确地找到所需的记录,为数据库性能提供了有力的保障。

五、总结

综上所述,B+树作为InnoDB的核心索引结构,通过多级目录和高效的节点管理,实现了快速的数据查找和管理。其高扇出、低树高的特性,使得即使在处理大规模数据时,查找过程仍然保持高效,通常不超过四层的树结构足以满足大部分应用需求。

通过B+树,InnoDB在庞大的数据集中能够快速、准确地找到所需记录,显著提高了数据库的性能和可靠性。其页目录、双向链表和单向链表等设计进一步优化了数据存储和查找过程,使得数据库系统在面对复杂查询时依然能够保持高效运作。

B+树不仅在数据组织和存储方面展现了强大的能力,更通过实际应用证明了其在数据库管理系统中的不可替代性。总之,B+树的引入和使用,使得InnoDB能够在现代数据库系统中脱颖而出,成为高性能、高可靠性的代名词。

 参考文献、书籍及链接

  • 《MySQL技术内幕:InnoDB存储引擎》(第2版):MySQL技术内幕 (豆瓣)
  • 《MySQL 是怎样运行的:从根儿上理解 MySQL》
  • 《Inside InnoDB: The InnoDB Storage Engine》:MySQL :: MySQL 8.0 Reference Manual :: 15 The InnoDB Storage Engine
  • 《InnoDB: The Ultimate Guide》:https://www.percona.com/blog/2018/06/05/innodb-the-ultimate-guide/
  • 《InnoDB Storage Engine Internals》:https://mariadb.com/kb/en/innodb-storage-engine-internals/
  • InnoDB的数据页结构

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

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

相关文章

财务会计与管理会计(六)

文章目录 高端费用查询图表VLOOKUP函数应用一段简单的VBA代码的应用 入库税金的二维分析SUMPRODUCT函数的应用 多姿多彩的数据表MOD函数的应用和万能表的应用 判断取值与查找取值的关系INDEX与ATCH函数在查找取值中的应用 在职期间项目分布统计表IF函数的应用 自动填充序号应用…

安卓中Room持久化库的使用

在Android开发中&#xff0c;Room是Google提供的一个持久化库&#xff0c;旨在为应用提供SQLite的抽象层&#xff0c;以简化数据库的访问和操作。相比直接使用SQLite&#xff0c;Room提供更清晰、更简洁的数据库访问机制。 1. Room的基础知识 1.1 引入Room依赖 首先&#xff…

STM32CubeMX学习记录——配置定时器

文章目录 前言一、学习目的二、CubeMX配置三、代码编写 一、学习目的 在STM32学习中&#xff0c;定时器是一个非常重要的组成部分。本文将详细记录如何通过CubeMX工具配置定时器&#xff0c;以实现1ms的定时功能。&#xff08;附计算公式&#xff09; 二、CubeMX配置 &#xf…

锂电搅拌设备实现产线可视化

锂离子电池生产过程中的搅拌设备是确保电池性能与一致性的重要环节。随着智能制造和工业4.0概念的深入发展&#xff0c;实现锂电搅拌设备的产线可视化与信息化已成为提升生产效率、优化产品质量、降低运营成本的关键路径。这一转变不仅要求技术上的革新&#xff0c;还涉及到管理…

如何在 .NET 中实现 SM3withSM2 签名:详细步骤和示例代码

下面是一个详细的示例&#xff0c;展示如何在 .NET 中实现 SM3withSM2 签名和验证&#xff0c;包括生成密钥对、计算哈希、签名和验证。示例使用了 BouncyCastle 库&#xff0c;你可以根据实际需求对代码进行调整。 1. 安装依赖库 使用 NuGet 安装 BouncyCastle 库&#xff1a…

ThinkPHP5.0.15漏洞解析及SQL注入

第一步&#xff1a; 通过查看5.0.15和5.0.16版本的对比&#xff0c;可以看到16版本对在Builder.php里面对数据库的增减做了修正&#xff0c;所以可以15版本的漏洞就存在在这里。这里的代码用的拼接的方式&#xff0c;就可以尝试使用报错注入来实现。 第二步&#xff1a; 我们…

音视频开发继续学习

RGA模块 RGA模块定义 RGA模块是RV1126用于2D图像的裁剪、缩放、旋转、镜像、图片叠加等格式转换的模块。比方说&#xff1a;要把一个原分辨率1920 * 1080的视频压缩成1280 * 720的视频&#xff0c;此时就要用到RGA模块了。 RGA模块结构体定义 RGA区域属性结构体 imgType&am…

一文搞定SQLite数据库

文章目录 SQLite数据库一、SQLite简介1、简介2、SQLite特性3、适用场景4、不适用场景5、如何选择 二、SQLite安装1、下载2、安装 三、SQLite基本语法1、数据库操作①、新建数据库②、查看数据库③、查看帮助指令 2、表操作①、新增表②、查看表信息③、查看表索引信息④、查看表…

dataV组件使用——数据更新更新组件

bug 当数据更新只更新一个属性页面不会刷新&#xff08;this.config1.data arr;&#xff09; 必须重新赋值整个config 方式一&#xff1a;检测到数据更新重新赋值config this.config1 {data: arr,header: ["所在单位", "人员姓名", "职位", &q…

【前端模式设计】js订阅发布模式之我见

一知半解最可怕 /*** description 订阅发布通知*/ export class SubscriptionPublish {private eventMap: Record<string, ((params: any) > any)[]>;constructor() {this.eventMap {};}on(key: string, handler: (params: any) > any) {if (!this.eventMap[key]…

星露谷模组开发教程#7 自定义机器

首发于Enaium的个人博客 添加大型工艺品 机器也算是大型工艺品&#xff0c;所以我们需要先添加它的大型工艺品。 这里做一张16x32格式为png的图。 if (e.Name.IsEquivalentTo("Data/BigCraftables")) {e.Edit(assets >{var dict assets.AsDictionary<string…

动手学深度学习——02深度学习介绍

AI 地图 X 轴&#xff1a;不同的模式&#xff08;越往右时间越新&#xff09; 符号学概率模型&#xff1a;统计学模型机器学习 Y 轴&#xff1a;问题领域&#xff08;先要了解一个东西&#xff0c;然后通过推理形成知识&#xff0c;最后做规划&#xff09; 感知&#xff1a;…

【C++】特殊类设计类型转换

目录 &#x1f4a1;前言一&#xff0c;特殊类设计1. 请设计一个类&#xff0c;不能被拷贝2. 请设计一个类&#xff0c;只能在堆上创建对象3. 请设计一个类&#xff0c;只能在栈上创建对象4. 请设计一个类&#xff0c;不能被继承5. 请设计一个类&#xff0c;只能创建一个对象(单…

Web3与医疗健康:去中心化技术在医疗行业的应用前景

随着区块链技术和去中心化理念的兴起&#xff0c;Web3作为新一代互联网技术正逐渐影响各个行业。在医疗健康领域&#xff0c;Web3技术的应用前景引起了广泛关注。本文将探讨Web3如何通过去中心化技术提升医疗健康行业的效率、透明度和安全性&#xff0c;并分析其在实际应用中的…

Docker 部署 SkyWalking 的指南

Docker 部署 SkyWalking 的指南 SkyWalking 是一款开源的应用性能监控工具&#xff0c;特别适用于分布式系统。通过 Docker 部署 SkyWalking&#xff0c;可以简化安装和配置过程。本文将详细介绍如何使用 Docker 部署 SkyWalking。 环境准备 在开始之前&#xff0c;请确保你…

LeetCode-3148. 矩阵中的最大得分

本人算法萌新,为秋招找工作开始磨炼算法,算法题均用python实现,如果我有哪些地方做的有问题的,还请大家不吝赐教. 1.题干 给你一个由 正整数 组成、大小为 m x n 的矩阵 grid。你可以从矩阵中的任一单元格移动到另一个位于正下方或正右侧的任意单元格&#xff08;不必相邻&…

小程序实现设备消息订阅

小程序实现设备消息订阅 一、说明 先说明一下什么是小程序的消息订阅&#xff0c;其实就是在小程序进行某个消息的订阅&#xff0c;订阅以后就可以在微信收到推送的消息了。推送走的服务端&#xff0c;在服务端调用微信的推送接口&#xff0c;然后推送至指定的微信用户。 二…

xlua热补丁

print("*********第一个热补丁***********")--直接写好代码 运行 是会报错的 --我们必须做4个非常重要的操作 --1.加特性 --2.加宏 第一次开发热补丁需要加 --3.生成代码 --4.hotfix 注入 --注入时可能报错 提示你要引入Tools--热补丁的缺点&#xff1a;只要我们修改…

无人值守变电站视频监控统一接入系统方案

目录 一、背景介绍 1、无人值守变电站 2、特点 二、需求分析 1、基本需求 2、需求分析 三、目标和网络 1、设计目标 2、系统网络建设目标 四、系统组成 1、总体架构图 2、前端采集层 &#xff08;1&#xff09;摄像头 &#xff08;2&#xff09;传感器 &#xf…

Qt——多线程

一、QThread类 如果要设计多线程程序&#xff0c;一般是从QThread继承定义一个线程类&#xff0c;并重新定义QThread的虚函数 run() &#xff0c;在函数 run() 里处理线程的事件循环。 应用程序的线程称为主线程&#xff0c;创建的其他线程称为工作线程。主线程的 start() 函数…