ICode9

精准搜索请尝试: 精确搜索
首页 > 数据库> 文章详细

MySQL是怎样运行的——第九章

2022-02-11 10:03:02  阅读:233  来源: 互联网

标签:第九章 FREE 碎片 链表 MySQL 空间 页面 INODE 怎样


这一章主要是关于表空间的内容。我们可以把表空间看作是页池

另外这一章的逻辑比较复杂,层级关系较多。在阅读书籍/我的博客时,一定要牢记这个顺序:表空间 > 组 > 段 > 区 > 页

9.1

这一节主要是复习前面章节的知识。
首先是常用的页面类型有哪些:
页面类型
其次是页面的通用部分。基本是任何种类的页面都有File Header和File Trailer。其中后者主要是用于校验。前者的组成相对比较复杂:
请添加图片描述

9.2

这一节内容很多。都是关于独立表空间结构的。
首先要介绍的是“区”这个概念。

每个区默认1MB。对于16KB的页来说,就是64个页。

之所以引入区的概念,是为了降低随机磁盘io。
传统的数据库都是使用机械硬盘的,机械硬盘的主要io开销在磁臂旋转上,如果是随机磁盘io,则磁臂会不停的转来转去,效率低。
如果想避免这种情况,就需要让磁臂尽量少的旋转,想要实现这种场景就需要让数据页在磁盘中连续存储。这样磁臂就不会乱转,只会一次往下转一点点。
而想要让磁盘中的页连续,就需要在申请空间的时候申请一大块磁盘空间,而非一个单独页那么大的空间,那样很有可能不连续。
因此,我们有了区的概念。一个区由64个页组成,以区为单位进行申请,就会很大程度上避免页的存储不连续问题。

而256个区放在一起又会形成“组”。

根据层级关系可知,每个组对应着大量的页面。而其中必然有一些页面是用来描述组的元信息以及区的元信息的(其实还有段的一些元信息)。描述这些信息的页是第一个组的前三个页和其他所有组的前两个页。

请添加图片描述
下面介绍一下这些页面都是干什么用的。

FSP_HDR:这个页面记录了表空间的元信息以及本组所有的区的元信息
XDES:和FSP基本相同,也是记录本组所有区的元信息。区别在于它不会再记录表空间的元信息了。
IBUF_BITMAP:用来存储change buff的元信息。change buffer主要是用来缓存操作的。对磁盘上页面的修改操作会被缓存在change buffer中,等到页面因为某些事件实际加载到内存中时再进行执行。原来change buffer只缓存插入操作,因此原来叫insert buffer。
INODE:记录了段的相关信息。段会在后面进行介绍。

之前提到区是一组页面的集合。对于B+树来说,一组页面里既有叶子节点,又有内部叶节点。如果区中什么都存储,则会让我们在遍历叶子结点的链表时效率很低。
因此,我们会把一类页面放到一些区中,另一类放到不同的区中。这种只存储一类页面的区的集合就叫段。从这里也不难看出,段是个逻辑概念

另外,段还不只是区,段是区和一些零散页面的组合。之所以这么说,是因为有些页面既不属于叶子结点那个区,又不属于内部叶节点那个区,可它们也应该被划分到段中。所以严格来说,段是区和零散页面的组合

那么什么样的页面不是内部叶子结点那个区,也不是普通叶子结点那个区呢?这里要引出一个概念,就是碎片区。

碎片区

由上文可知,内部叶节点区的集合 和 叶子结点区的集合分别对应一个段。可是即使每种页面只有一个区,那也需要2MB的空间。如果一个表里还没什么记录就要2MB的空间,那也太多了吧??

因此,当每个表刚开始有数据的时候,我们会把这些数据存放到碎片区的页面里。碎片区里什么类型的数据都有,既有内部叶节点,又有叶子结点。这也就意味着碎片区里的数据属于不同的段

所以,每个段中一开始的一些数据,严格来说是前32个页面,都是来源于碎片区的。之后如果还有数据,则会实际分配一个又一个的区域区存储他们。

到这里,我们介绍了碎片区的概念,才能往下介绍区的类别。

区的分类

区可以被分为以下几类:

  • 空闲区:完全崭新的区域,好像白纸。
  • 有剩余空间的碎片区:掺杂着数据的碎片区,新的数据会插入到这里的页面中。
  • 无剩余空间的碎片区:都满了。
  • 附属于某个段的区:就是普通的区,里面的页面都属于一个段。比较“专一”

四种类别也叫四种state,对应英文名FREE、FREE_FRAG、FULL_FRAG、 FSEG

这些状态存储在哪里呢?存储在表示区域元信息的结构体里,这种结构体叫XDES ENTRY。每个结构体都对应一个区。
在这里插入图片描述
这些字段的作用如下:

  • SegmentID:表示这个区所属段的编号。如果该区不属于某个段则这个编号没意义。碎片区就不属于什么段对吧?
  • ListNode:这个部分是链表。可以把自己和兄弟们连起来。
  • State:上边介绍过了,主要是说明区的状态
  • PageStateBitmap:一共128位,每个页占用2位。第一位表示页是否空闲,第二位还没用到呢。

额外补充一下ListNode结构。这个结构细分来看是两组属性,每一组都有一个页号和页内偏移。通过页号和页内偏移可以唯一的定位一个XDES Entry。

XDES链表

上文中我们提到了XDES链表,为什么需要这样的结构呢?
想象这样一个场景,你想要新增一条记录到碎片区。如果有没用完的碎片区,可以直接把数据存到那个碎片区的页里。如果都用完了呢?你需要申请一个崭新的碎片区,然后把数据存到其中的某个页里。在这过程中你还要修改对应的区的状态。

可是我们怎么知道哪些区是有空位的,哪些区不是呢?如果有几千个区,难道要一个一个遍历找空闲区吗?很显然不能。
因此,InnoDb之父把崭新的区、没用完的碎片区、用完的碎片区各自拿链表穿起来,形成了FREE、FREE_FRAG、FULL_FRAG这三条链表。

然而还有一类问题没解决,就是附属于某个段的区。附属于某个段的区经常需要根据段信息被检索到,因此应该以段号为依据,把它们也串联起来。可是附属于某个段的区也有崭新的、空闲的和已满的,因此每个段都要对应三个链表,分别叫FREE、NOT_FULL、FULL

这里简单整理一下:

每个索引都有叶子结点和内部叶节点。也就是说每个索引至少对应两个段。
假设我们的表里有两个索引,最起码有4个段,全表有4*3+3个链表。

最后有个小问题是如何找到这些链表。想唯一定位一个链表里的xdes entry节点,需要页号和页内偏移。innodb设计者干脆设计了这样的结构:
在这里插入图片描述
如图所示,这种结构叫做List Base Node。里面有链表长和定位链表首尾节点的属性。

段的结构

段是区和零散页面的集合。描述段的元信息也需要一些结构体。我们把这种结构体叫做INODE entry。
在这里插入图片描述

  • 段号:没啥说的,必须有
  • NOT_FULL_N_USED:未用完区的链表中已经使用了多少页面
  • 三个链表的首尾节点:必须有
  • Magic Number:标识这个INODE Entry是否已经被初始化了
  • Fragment Array Entry:之前说过段是区和零散页面的集合,那32个零散页面因为都是碎片区的,所以不在链表里。单独有32个属性记录这些页的页号。

各类页面详细情况

前面的文章中有些信息没怎么介绍,比如XDES ENTRY的链表到底存放在什么地方。下面将继续介绍这些细节。

FSP_HDR

固定是每个独立表空间的第一个页面。它存储了表的元信息以及第一个组里的256个XDES ENTRY。(这里要弄清一件事情,链表只是在逻辑上表示第几个区的entry是哪一类的。比如第1 3 5 7 9个entry是FULL的。但它们在物理上还是连续存储在每一组的第一个页面中的。)
在这里插入图片描述
Header和Trailer已经说很多次了。EmptySpace就是没用的空间。XDES ENTRY占用的那部分空间也介绍过了。剩下的就是File Space Header了。
在这里插入图片描述
下面介绍一下各个字段的用处。

  • SpaceID:一看就懂
  • Not Used:一看就懂
  • Size:当前表空间拥有的页面数
  • FRAG_N_USED:表明在FREE_FRAG中已经使用的页面数量
  • List Base Node for FREE/FREE_FREG/FULL_FRAG List:表空间所拥有的三个链表的首尾节点位置
  • FREE_LIMIT:表空间对应的那个磁盘文件是自动扩容的。自动扩容每次都会扩容不少,这导致大部分表空间内的区都是空闲的。如果把这些区全部加入到表空间级的FREE链表里,那表空间的FREE链表就太长了,而且这样的操作本身就很费时间,更何况通常这些崭新的页也都是在物理上连续的。因此InnoDb之父用FREE_LIMIT字段表示一个页号。这个页号以后的页都是崭新的页。
  • Next Unused Segment ID:下一个没使用的段号。在创建新的段时就不用遍历所有的Inode Entry结构了。
  • Space Flags:表空间级的一些属性,详情如下(现在不用一个一个记,没用)在这里插入图片描述
  • List Base Node for SEG_INODES_FREE/FULL List:每个段对应的inode entry都会存放到INODE类型的页面中。如果一个INODE页面存不下,就需要多个INODE页面,这些INODE页面会形成链表。这两个属性就是记录两种链表的首尾节点的。(两种很好理解吧?一种是满的,一种是不满的)

XDES

这个类型的页面就是除第一组以外其他组存放XDES ENTRY的页面。
在这里插入图片描述
可以看到,没有Space Header那部分。剩下的结构都是一样的。

IBUF_BITMAP

这类页面就是存储一些有关change buffer的信息。change buffer在这本书里基本不会介绍,唯一的一点介绍已经写在前面了,简单来说就是更新操作的缓冲池。

INODE

这类页面是存放INODE ENTRY的。每一组的第三个页面是INODE类型的。
在这里插入图片描述
结构如图所示。文件头、尾、空区间就不再介绍了。
一个INODE页面可以存放85个ENTRY,这个规则在介绍XDES和FSP的时候已经涉及到了。多个INODE页面会形成链表。链表分两种,刚才已经介绍过了,存放在FSP页面的space header里。
上文也提到了,唯一定位一个entry需要靠页号和页内偏移。所以定位前后两个entry需要四个属性,也就是List Node for INODE Page List。

这样在创建段的时候,会先去看空闲inode页面链表,找一页插入新的inode entry。如果没有空闲页面了则需要把崭新的页面变成INODE ENTRY空闲页面。

如何通过段找到对应的INODE

B+树对应两种段,如何通过B+树定位到段的inode结构呢?定位INODE结构需要页号+页内偏移,那这个数据存在哪呢?答案是存到B+树根节点所在页面里。在B+树(也就是Index类型的页面)页面的File Header下面,有一个Page Header字段,里面有PAGE_BTR_SEG_LEAF和PAGE_BTR_SEG_TOP两个子字段,这两个子字段就分别存储了叶子节点和内部叶节点的段头信息(其实还存储了表空间id)

在这里插入图片描述

9.3

这一节主要记录系统表空间。
其实和独立表空间差不多,主要差距在第一个组的前几个页面上。
在这里插入图片描述
可以看出,除了第一组的第一个区有几个页面不一样之外,其他的都差不多。
特有的页面如下所示:
在这里插入图片描述
在这里,我们只简单介绍一下数据字典头部信息。

InnoDB数据字典

MySQL会帮我们保管插入的用户记录。对外来说也许可以看成是记录的池子,但对内来说,插入记录是很复杂的。要校验表信息正确不正确,列正确不正确,对应的索引是哪个表空间的,具体的页面在哪,等等。
这些数据也就是元数据,InnoDB定义了一些表去存储这些元数据。
在这里插入图片描述
在这里插入图片描述
这些记录元信息的系统表叫做数据字典。其中最重要的是TABLES、COLUMNS、INDEXSES、FIELDS这四个系统表。

SYS_TABLES

在这里插入图片描述
这个表有两个索引,分别是NAME为主键建立的聚簇索引和以ID为主键建立的二级索引。

SYS_COLUMNS

在这里插入图片描述
这个表有一个聚簇索引,是以TABLE_ID和POS为主键的聚簇索引。

SYS_INDEXES

在这里插入图片描述

SYS_FIELDS

在这里插入图片描述

Data Dictionary Header页面

有了这四个基本表,就可以获取其他系统表和用户自定义的表的元数据。
那么这四个基本表的元数据去哪找呢?不能套娃,只能硬编码到代码里了。就是我们之前看到的系统表空间中的第七个页面,这个页面会固定的存储这些信息。

在这里插入图片描述
注意这里的段头不是B+树里记录内部叶节点和叶子结点对应的段的INODE信息的段头。我们这个第七页是数据字典段

接下来其实主要就是介绍一下Data Dictionary Header

  • Max Table ID:最大的表id,每次新建表这个数值都加1
  • Max Index ID:每次创建索引这个值就加1
  • Max Row ID:每个表都默认有聚簇索引。如果不定义主键也没有非空的unique列,就会用row id来做聚簇索引。这个值就是记录最大的row id。
  • Max Space ID:每次创建表空间都会把这个值+1
  • Mix ID Low(Unused):还没用
  • Root of …:表示那四张最重要的的系统表的索引的根页面。因为Table表有两个索引,所以加起来是5个root。

最后要注意的是这些表是不能直接访问的,但有些数据又有用,因此在information_shcema里提供了一些以INNODB_SYS开头的表,这些表会读取系统表的信息,给我们查询用。它和系统表多少有些差异,但是够用了。

标签:第九章,FREE,碎片,链表,MySQL,空间,页面,INODE,怎样
来源: https://blog.csdn.net/Ning862217083/article/details/122850453

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有