存放页的大池子——InnoDB表空间

news2024/12/23 6:39:13

在上一章节中,我们知道了提到了表空间,表空间里面没有直接存放表,有的是许多许多的页,我们也说了表空间的分类,有系统表空间,独立表空间,其他表空间。

每个新建的表都会在对应的数据库目录里面添加一个 表明.ibd 的文件,这个文件里面存储了表中的数据索引。一旦我们想要位某个表插入一条记录的时候,就从池子里面捞出来一个对应的页来把数据给写进去,本节内容深入到表空间中,了解各个细节。

复习一些知识

页有很多类型,我们之前说的是数据页,也叫索引页,也叫INDEX类型的页,INDEX类型的页由7个部分组成,其中的两个部分是所有页的通用的,分别是File Header ,File Trailer ,File Trailer 用来检验页是否完整,保证从内存到磁盘刷新时内容的一致性,File Header是用来记录页的一些基本信息,具体如下:

 注意事项一:FIL_PAGE_OFFSET ,表示的页号,由4个字节组成,也就是4*8 = 32个二进制位,所以可以有2 的32次方个页,一个页是16kB,那么一个表空间最多是64TB,表空间的第一个页的页号是0,之后的页分别是1,2,3,4,5等等。

注意事项二:某些类型的页可以组成链表,链表中的页可以不按照物理顺序存储,而是根据FIL_PAGE_PREV和FIL_PAGE_NEXT来存储上一个页和下一个页的页号。需要注意的是,这两个字段主要是为了INDEX类型的页,也就是说我们之前一直说的数据页建立B+树之后,为每层节点建立双向链表用的,一般类型的页是不适用这两个字段的。

每个页的类型是由FIL_PAGE_TYPE表示的,不同类型的页,在该字段上面的值是不同的。

什么是区?什么是组?区是为了解决什么问题?组又是为了解决什么问题?

表空间中的页太多了,为了更加方便的管理这些页,区就出现了。 连续的64个页就是一个区(extend),一个组占用的1MB的空间。 无论是系统表空间还是独立表空间,都可以看做是若干个区组成的。 每256个区被划分为一个组

每个组的头几个页的类型都是相似的

 右边的每一行都是一个页,之前我们听说过有好多类型的页,但是只学过INDEX页,现在除了数据页他们都出来了

表空间被划分为许多连续的区,每个区默认由64个页组成,每256个区被划分为一组,每个组的最开始的几个页的类型是固定的。

回到最初的问题,区是为了解决什么样的问题?

我们先来分析一下之前没有区的时候,InnoDB是如何查找目录页的?是如何插入数据的?插入数据的本质上就是向该表的聚簇索引以及所有的二级索引代表的B+树节点中插入数据。 而B+树每一层的页都会形成一个双向链表,如果是以页为单位来分配存储空间的话,双向链表相邻的两个页之间的物理结构可能离的非常远。 我们介绍B+树索引使用场景的时候,特别提到范围查询只需要定位到最左边的记录和最右边的记录,然后沿着双向链表一直扫描就可以了,但是如果两个页物理位置相连的太远了,那就是所谓的随机I/O,随机I/O 是非常慢的,所以我们应该让B+树同一层的相邻节点 在物理位置上面也相邻,这个时候进行范围查询的时候才可以使用所谓的随机I/O。 

所以,我们才引入了区,不要忘记了区的定义,区是连续的64个页。 在表中数量很大的时候,为某个索引(B+树)分配空间的时候,直接给该索引以区为单位分配空间,分配区,就是分配连续的页,这样可以消除随机I/O,提高访问速度,虽然可能造成了一点点的空间的浪费(B+树无法填满整个区)。 

什么是段?段是为了解决什么难题出现的?

现在我们有了区,进行范围查询的时候,就是对B+树叶子节点中的记录进行查找,范围查询的结果可能是分布在多个不同的页中,也就是多个不同的叶子节点中,这些叶子节点是存放在一个区中的,或者是存放在不同的区中,如果是不同的区,那空间就不是连续的了, 这个时候,我们还要尽可能的降低不连续性,如何降低不连续性? 我们需要把查询的区的数量变少,这样就查询的快一些。  这个时候,我们可以把叶子节点也非叶子节点进行区别对待,也就是说叶子节点有独有的区,非叶子节点有独有的区。 存放叶子节点的区的集合就算是一个段, 存放非叶子节点的区也算是一个段,也就是说,一个索引会生成两个段,一个叶子节点段,一个非叶子节点段。 

所以,概况来说,段是为了加快查询速度,具体体现在范围查询的时候,数据分散在不同的区中,区是连续的空间,不同的区可能是不连续的,区中的存储是很纯粹的,所以我们可以把区给分开,只把存放叶子节点的区放在一起,这样通过双向链表查询的时候,速度会加快,放在一起的这些区就组成了一个段。

默认情况下,使用InnoDB的存储引擎只有一个聚簇索引,一个索引有两个段,段是以区为单位申请存储空间的,一个区默认1M空间,所以默认情况下一个只存储了几条记录的小小的表也要占用2M的空间吗? 以后每次为这个表添加一个索引都要多申请2M的存储空间么? 这对于存储记录比较少的表,对空间的浪费比较严重。 但是问题出现在,我们现在的区都是十分纯粹的,一个区被整个分配给某一个段,也就是说区中的所有页都是为了存储同一个段的数据而存在的,这样即使段中的数据填不满区中的页,也不能挪作他用。

于是InnoDB的大佬提出了碎片区的概念,那么碎片区的概念就很明显了。在该区的页中,这些页不是为了存储同一个段的数据而存在的,有的页存储了段A,有的页存储了段B,有些页甚至那个段都不属于,碎片区直属于表空间,不属于任何一个段。 所以此后为某个段分配存储空间的策略是这样的:

在刚开始向某个表中插入数据的时候,也就是向该表对应的B+树插入数据的时候,该B+树对应的两个段是从碎片区的某个页为单位进行存储空间的。

当某个段已经占用了32个碎片区之后,就会以完整的区为单位来分配空间。

现在段不能简单定义为某些区的集合,更加准确的应该是某写零散的页,以及一些完整的区的集合。 除了索引的叶子节点段和非叶子节点段之外,InnoDB中还有一些存储特殊数据而定义的段,比如说回滚段等等。

区有哪些分类?为什么要给区分类?

表空间是由段组成的,段中存放的某些区的集合,以及一些零散的页,这些区可以分为4种类型:

空闲的区:到现在为止还没有用到这个区中的任何页。

有剩余空间的碎片区:表示碎片区还有可用的页。

没有剩余空间的碎片区:该碎片区的所有的页都被使用了,没有空闲的页。

附属于某个段的区:每个索引都可以分为叶子节点段和非叶子节点段,除此之外,InnoDB还会有另外一些特殊作用的段,这些段的数据量很大的时候将使用区作为基本的单位分配。 

我们再来看一下表空间的结构,牢记区在表空间中是如何存放的

 我们看到图片上面的XDES,这是一个页,是和数据页同等级的页,大小是16k; 除了第一个组,后面的每个组的256个区(一个区有64个页)都有XDES页,全称是extent descriptor,用来登记本组的256个区的属性,所以这个页就是管理自己所在区的,所有的区都有这个页。 

我们之前分析过数据页,现在我们来详细看一下XDES Entry

这个东西不是一个页,所以页就没 File Header和File Tralier了,这个东西只是一个结构体XDES Entry,是页中一部分,每个区都对着一个此结构。 

Segement ID 表示该区所属与某个段的id,只有该区被分配了段该字段才会有意义。

List Node:这个部分可以讲若干个XDES Entry串联成一个链表

 表空间是中存放的是组,组中是区,区中是页,页中有XDES,那么所有页中的XDES 如果链接成一个链表的话,那么如果我们想要定位表空间中的某一个位置的话,只需要指定页号以及该位置在指定页号中的页内偏移量即可。 

Pre Node Page Number 和 Pre Node Offset 的组合就是指向前一个 XDES Entry的指针。

Next Node Page Number 和 Next Node Offset 的组合就是指向后一个的 XDES Entry的指针。

State 这个字段表明区的状态,可选值就是上面的四个 FREE , FREE_FRAG , FULL_PRAG和FSEG 。

Page State Bitmap
这个部分共占用16个字节,也就是128个比特位。我们说一个区默认有64个页,这128个比特位被划分为64个部分,每个部分2个比特位,对应区中的一个页。比如Page State Bitmap部分的第1和第2个比特位对应着区中的第1个页,第3和第4个比特位对应着区中的第2个页,依此类推,Page State Bitmap部分的第127和128个比特位对应着区中的第64个页。这两个比特位的第一个位表示对应的页是否是空闲的,第二个比特位还没有用。

定位表空间的中的某个位置干嘛呢?也就是说组成这个链表是干嘛用的?

到现在为止,我们已经了解了区,段,碎片区,XDES等等的概念,不要忘记了我们最初的目的,只是想插入一条数据的时候保证效率又不至于数量少的表浪费空间。现在我们知道了向表中插入数据,本质上就是想表中的各个索引的叶子节点段,非叶子节点段插入数据,不同的区有不同的状态。 现在我们重新捋一下插入数据的过程:

1. 当段中数据较少没有占满32个页的时候,先查看表空间中是否有状态为FREE_FRAG的区,如果找到了,那么从该区里面取出一些零碎的页插入进去。 否则的话,到表空间中申请一个状态为Free的区,然后让该区变成FREE_FRAG的区,然后从该新申请的区把一些零碎的页把数据给插入进去。 之后不同的段使用零碎页的时候都会中该区中取出数据,直到该区没有零碎页了,那么把该区变成FULL状态。 

此处有个问题,就是我们如何知道表空间里面哪些区是FREE的,哪些区是FREE_FRAG的,哪些区是FULL_FRAG的?表空间的不断增大的,当增长到GB的时候,区的数量也就上千了,我们不能每次从头遍历这些区对应的XDES Entry结构 的State字段来进行判断吧? 那是如何定位空闲区的呢? 这个时候 XDES Entry 中的List Node 部分就发挥效果了,我们通过 List Node中的指针,做三件事情:把状态是FREE的区对应的XDES Entry结构通过 List Node连接成一个链表,叫做FREE链表;同理FREE_FRAG链表;同理 FULL链表。

这样每当我们想找一个FREE_FRAG状态的区时,就直接把FREE_FRAG链表的头节点拿出来,从这个节点中取一些零碎的页来插入数据,当这个节点对应的区用完时,就修改一下这个节点的State字段的值,然后从FREE_FRAG链表中移到FULL_FRAG链表中。同理,如果FREE_FRAG链表中一个节点都没有,那么就直接从FREE链表中取一个节点移动到FREE_FRAG链表的状态,并修改该节点的STATE字段值为FREE_FRAG,然后从这个节点对应的区中获取零碎的页就好了

2. 当段中数据已经占满了32个页了,那么之后的话直接是以区为单位进行空间申请。

此处有个问题,当我们插入之前,我们怎么知道哪个区是属于哪个段的呢? 我们总不能拿那些已经归属于某个段,状态为FSEG的区来使用吧。 所以我们把FSEG 的区对应的XDES Entry结构都独立的加入一个链表中?  这样是不行的,不同索引的不同的段是不能共用同一个区的。  我们需要每个段都有他独立的链表,所以我们可以根据段号(Segment ID)来建立链表,与此同时,一个段里面的区也有好多,有时完全空间的,有慢的,有处于两者之间的,所以每个段里面的区,根据XEDS Entry结构建立了三个链表,FREE链表,NOT_FULL链表,FULL链表。 

如何找到这些链表呢?什么是链表基节点呢?链表基节点存储在哪个地方呢?

我们上面介绍了很多的链表,但是如何找到这些链表呢? 如何找到这些链表的头结点或者是尾节点呢? 其实是通过List Base Node 结构体找到的,结构体如下

 我们上面说的每一条链表都有一个对应的这样的结构,并且放在固定的位置,那我们就很容易定位到上面所说的所有链表了。

List Length :该链表的长度

First Node Page Number 和 First Node Offset 表明该链表的头结点所在表空间的位置

Last Node Page NumberLast Node Offset表明该链表的尾节点在表空间中的位置

至于链表基节点存储在哪里,我们后面再说。 

段的结构是什么?

我们前面对段的定义是,若干个零散的页以及一些完整的区,段不是表空间中的连续的物理区域,是一个逻辑上的概念。  就像是每个区都有对应的XDES Entry 来记录这个区的属性一样,InnoDB的官方也设计了每个段都有一个 INODE结构来记录段中的属性一样

 Segment ID:段的id

NOT_FULL_N_USED:NOT_FULL链表已经使用了多少个页。 下次我们需要从NOT_NULL链表中分配空闲页的时候可以直接根据这个字段定位到。不用从链表第一个页开始查找了。

3个List Base Node:分别对应FREE链表,NOT_FULL链表,FULL链表 的基节点,可以快速定位到某个段的某个链表的头尾节点。 

Magic Number :用来标记 INODE Entry 是否已经被初始化。 

Fragment Array Entry : 每个Fragment Array Entry结构都对应着一个零散的页,这个结构一共4个字节,表示一个零散页的页号。

上面我们分析了各种的结构,但是我们始终不知道这些结构都是在哪个地方存放的,现在我们来看一下吧。 

FSP_HRD 类型 

第一个组的第一个页就是这个类型,这是我们除了index页之后的新页,它里面存储了表空间的整体属性和第一个组里面的256个区对应的XDES Entry 结构。

 File Header和File Trailer就不用再说了。 

File Space Header 用来存储表空间的整体信息。

XDES Entry 存储本组的256个区的属性信息

Empty Space 顾明思议,空的空间

重点来看File Space Header ,用来存储表空间的整体属性,看下具体结构

 Space ID : 表空间id

Not Used : 未被使用

Size : 当前表空间占用的页数

FREE Limit  尚未被初始化的最小页号,大于或等于这个页号的区对应的XDES Entry 结构都没有被加入FREE链表 。 表空间对应的是具体的磁盘文件,一开始创建表空间的时候对应的磁盘文件都没有数据,所以我们需要对表空间进行一个初始化,包括为表空间中的区建立一个XDES Entry 结构,为每个段建立INODE Entry结构,建立链表等等操作。  我们可以一开始为表空间申请特别大的空间,但是实际上大部分时候里面的所有的区都是空闲的,我们可以把所有空闲的区的XDES Entry 结构加入链表,也可以选择只把一部分空闲区对应的XDES Entry 加入链表,等什么时候空闲链表中的XDES Entry结构对应的区不够使了,再把之前没有加入FREE链表的空闲区对应的XDES Entry结构加入FREE链表,中心思想就是什么时候用到什么时候初始化,设计InnoDB的大佬采用的就是后者,他们为表空间定义了FREE Limit这个字段,在该字段表示的页号之前的区都被初始化了,之后的区尚未被初始化。

Space Flags 表空间中一些占用存储空间较小的属性

FRAG_N_USED  FREE_FRAG链表中已经使用的页的数量

List Base Node for Free List :表空间的FREE链表基节点

List Base Node for FREE_FRAG List :FREE_FREG链表的基节点

List Base Node for FULL_FRAG List :FULL_FREG链表的基节点

Next Unused Segment ID : 当前表空间中下一个未使用的 Segment ID ,方便为之后的表空间初始化赋值。

List Base Node for SEG_INODES_FULL List  :SEG_INODES_FULL链表的基节点

List Base Node for SEG_INODES_FREE List  SEG_INODES_FREE链表的基节点

XDES Entry 部分

学了半个这个结构,现在才知道,原来这个结构是表空间中的第一个页里面存储的,第一个页里面会存储256个XDES Entry,分别对应改组里面的256个区。 

XED类型

这个类型和上面的FSP_HRD相似,除了少了关于表空间的 File Space Header部分

IBUF_BITMAP 类型 

暂时省略

INODE类型

此类型只位于第一个组的第一个区的第三个页中,里面存储的是关于表空间中的段的信息。 每个段都有一个INODE  Entry 结构 。 我们看下一INODE 类型页的结构

INODE Entry 部分,已经介绍过了,存储的是零散的页的地址以及属于该段的FREE,NOT_FULL,FULL链表的基节点。 

 List Node For INODE Page List :

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

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

相关文章

性能优化之影响分析

页面性能的影响 性能非常重要,而具体反映到我们的业务场景中,可能会有如下影响: 不利于用户留存 站点页面的展现速度非常影响用户体验,很多用户会因等待的不耐而放弃站点。研究表明,47 % 的消费者希望页面能够在 2s …

chatgpt赋能Python-python_bonjour

Python Bonjour: 通过Python实现Bonjour协议 介绍 Bonjour是苹果公司推出的一种新型、免配置的网络协议,它可以使局域网中的电脑、打印机、电话等各种设备自动发现及配置,从而促进了网络设备的普及。Python作为一种高效、简洁、易学的语言&…

【WSN覆盖】基于麻雀搜索算法的三维混合无线传感器网络覆盖优化 三维WSN覆盖空洞修复【Matlab代码#25】

文章目录 【可更换其他算法,获取资源请见文章第6节:资源获取】1. SSA算法2. 节点感知模型3. 三维混合WSN覆盖问题4. 部分代码展示5. 仿真结果展示6. 资源获取 【可更换其他算法,获取资源请见文章第6节:资源获取】 1. SSA算法 2. 节…

机器学习与深度学习——通过奇异值分解算法压缩图片

机器学习与深度学习——通过奇异值分解算法压缩图片 什么是奇异值分解? 奇异值分解(Singular Value Decomposition,SVD)是一种重要的线性代数方法,用于将一个矩阵分解成三个部分的乘积形式。它的应用非常广泛&#x…

Leecode98

leecode98 验证二叉搜索树 给定一个二叉树,判断其是否是一个有效的二叉搜索树。 假设一个二叉搜索树具有如下特征: 节点的左子树只包含小于当前节点的数。 节点的右子树只包含大于当前节点的数。 所有左子树和右子树自身必须也是二叉搜索树。 &#x1…

【Linux】shell编程—Here Document免交互

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 一、免交互概述二、免交互常规用法1.read 命令的读取2.wc -l 的内容行数统计3.passwd用户密码的修改4.cat 查看内容并输出到文件中5.cat 查看交互内容并输出到新的文…

Spring传播机制(七种)

一、概述 Spring在TransactionDefinition接口中规定了7种类型的事务传播行为。Propagation枚举则引用了这些类型,开发过程中我们一般直接用Propagation枚举。 1.1 Propagation源码 public enum Propagation {REQUIRED(0),SUPPORTS(1),MANDATORY(2),REQUIRES_NEW(…

redis源码浅析-ziplist实现

redis中的list是有多种实现的,其中一种是ziplist,其介绍如下 ziplist 是一个经过特殊编码的双向链表,旨在提高内存效率。 它存储字符串和整数值,其中整数被编码为实际整数而不是一系列字符。 它允许在 O(1) 时间内在列表的任一侧进…

戴尔 Dell Latitude E7480电脑 Hackintosh 黑苹果efi引导文件

原文来源于黑果魏叔官网,转载需注明出处。(下载请直接百度黑果魏叔) 硬件配置 硬件型号驱动情况 主板戴尔 Dell Latitude E7480 处理器Intel Core i7-7700U已驱动 内存8GB 2133MHz DDR4 * 2已驱动 硬盘Sandisk 1T M.2 NVMe SSD已驱动 显…

windows解决python安装django架构没有django-admin命令

目录 一.尝试安装与配置 1.直接pip命令安装 2.用pycharm测试 3.官网下包安装 二.解决 1.找到django安装的路径 2.配置系统变量 3.测试创建项目 3.1.执行访问页面 3.2.解决 3.3.继续测试 4.pycharm打开 一.尝试安装与配置 1.直接pip命令安装 pip install django dja…

chatgpt赋能Python-python_7

Python 7 - 探索最受欢迎的Python版本 如果你是一位Python开发人员,你就会知道Python 7是什么。Python 7是Python编程语言的第7个主要版本,它带来了许多新的功能和改进,包括内置类型注释、上下文变量、字节码优化和更多。 在本文中&#xf…

度小满科技金融迭代:普惠小微与技术创新并肩而行

2015年,深度贫困乡镇隘口镇推出“本土人才”政策,呼吁大学生人才下基层,助力乡村经济振兴工作。 叶茂的人生轨迹因此而发生改变。彼时,20多岁的叶茂果断辞去药材公司的高薪工作,开始了回乡种植黄精的创业路,并成功竞选东坪村村主任…

log4cpp的安装(vs/visual studio版)

log4cpp安装下载源visual studio安装方法 log4cpp安装 下载源 log4cpp visual studio安装方法 将下载好的压缩包解压缩后得到下列目录(解压缩的文件夹最好放C盘,不然在编译阶段往往会出现链接不上的问题) 找到mscv10文件夹内的mscv10.sl…

【概念篇】浅谈 AOP、OOP、DDD、IOC

前言 在招聘要求中,有没有经常看见,对AOP、OOP、DDD、IOC有一定的认识,能够自主开发模块,这一类的要求。听起来是不是挺高大上,然后百度一搜,给你出来一堆概念性的东西——结果就是,东西…

《LKD3粗读笔记》(14)块I/O层

什么是块设备? 系统中能够随机访问固定大小数据片的设备被称为块设备,这些数据片称作块,最常见的块设备是硬盘。什么是字符设备? 字符设备按照字符流的方法被有序访问,像串口和键盘就都属于字符设备。这两种设备的区别…

从零开始搭建 Lsky Pro 兰空图床

本文源码:https://github.com/chen2438/chenhaotian.top/tree/main/source/_posts/linux-app/lsky.md 在我的博客上查看:https://chenhaotian.top/2022/12/30/linux-app/lsky/ 从零开始搭建 Lsky Pro 兰空图床 官方教程 安装环境 使用OneinStack安装…

第五章 面向对象-8.enum 枚举

enum 枚举 枚举的实例对象是固定的,实例是自动new,每个枚举类会自动继承java.lang.Enum 抽象类 如何声明枚举? 所有枚举类都是java.lang.Enum的子类,无需用extends来继承,如下是枚举类拥有的常用方法 方法名称说明…

测试报告框架 —— Allure2测试报告

目录 Allure2测试报告 1、使用 Allure2 运行方式-Python 2、使用 Allure2 运行方式-Java 3、生成测试报告 4、Allure2 报告中添加用例标题 5、allure2报告中添加用例步骤 6、allure2报告中添加用例链接 7、allure2报告中添加用例分类 8、Allure2 报告中添加用例描述 …

【面试题】前端必修-浏览器的渲染原理

大厂面试题分享 面试题库 前后端面试题库 (面试必备) 推荐:★★★★★ 地址:前端面试题库 web前端面试题库 VS java后端面试题库大全 1.浏览器的渲染原理 #浏览器是如何渲染页面的 1.什么叫渲染 render 当我们输入一个url地址的…

新书上市 | 从大脑认知开始,全方面提高编程能力,助你摆脱“GPT焦虑症”

目录 一、ChatGPT火爆全网二、《程序员超强大脑》三、本书内容四、本书特色五、作译者简介1、费莉安赫尔曼斯(Felienne Hermans)2、蒋楠 大家好,我是哪吒。 🏆本文收录于,49天精通Java从入门到就业。 全网最细Java零…