段的结构
- 上一节说过表空间分为各个段,每个段里面又是以区为单位,每个区则有64个页。区根据剩余存储空间分为:Free,FREE_FRAG,FULL_FRAG 三种类型,为了方便管理区,给每个区创建XDES Entry结构,然后给这三种类型创建三个对应的XDES Entry结构链表。
- 像每个区都有对应的XDES Entry来记录这个区中的属性一样,设计InnoDB的大佬为每个段都定义了一个INODE Entry结构来记录一下段中的属性,如下图:
- Segment ID:就是指这个INODE Entry结构对应的段的编号(ID)
- NOT_FULL_N_USED:这个字段指的是在NOT_FULL链表中已经使用了多少个页。下次从NOT_FULL链表分配空闲页时可以直接根据这个字段的值定位到。而不用从链表中的第一个页开始遍历着寻找空闲页。
- List Base Node:分别为段的FREE链表、NOT_FULL链表、FULL链表定义了List Base Node,这样我们想查找某个段的某个链表的头节点和尾节点的时候,就可以直接到这个部分找到对应链表的List Base Node
- Magic Number: 这个值是用来标记这个INODE Entry是否已经被初始化了(初始化的意思就是把各个字段的值都填进去了)。如果这个数字是值的97937874,表明该INODE Entry已经初始化,否则没有被初始化。
- Fragment Array Entry:对应着一个零散的页,这个结构一共4个字节,表示一个零散页的页号。
各类型页详细情况
- 上一节我们只是粗略的讲解了各个页的介绍,下面我们就来讲讲为什么会有这些页
FSP_HDR类型
- 首先我们先来看看第一组的第一个页,页号为0,由上面可以知道这个页的类型是 FSP_HDR类型
- FSP_HDR类型 主要存储的试表空间的一些整体属性和第一个组的 0- 256 个区对应的 XDES Entry 结构,下图是他们的结构:
- File Header:文件头部,主要是页的一些通用信息
- File Space Header:表空间头部,表空间的一些整体属性信息
- XDES Entry:区的描述信息,存储本组256个区的属性信息
- 一个XDES Entry结构的大小是40字节,但是一个页的大小有限,只能存放有限个XDES Entry结构,所以我们才把256个区划分成一组,在每组的第一个页中存放256个XDES Entry结构。
- Empty Space:结构填充无实际意义
- File Trailer:校验页是否完整
- 像 File Header ,File Trailer,Empty Space 这些我们之前都讲过我们就不再次重复了,这里我们主要讲解 File Space Header 这个部分
File Space Header
- 这个部分主要作用是存储表空间的一些整体属性,如下图:
- Space ID:
- 占用4字节
- 表空间的ID
- Size:
- 占用4个字节
- 当前表空间占有的页数
- FREE Limit:
- 占用4个字节
- 尚未被初始化的最小页号,大于或等于这个页号的区对应的XDES Entry结构都没有被加入FREE链表
- Space Flags:
- 占用4个字节
- 表空间的一些占用存储空间比较小的属性,主要是一些布尔类型的属性:
- POST_ANTELOPE:占用1bit 表示文件格式是否大于ANTELOPE
- ZIP_SSIZE:占用4bit 表示压缩页的大小
- ATOMIC_BLOBS:占用1bit 表示是否自动把值非常长的字段放到BLOB页里
- PAGE_SSIZE:占用4bit 页大小
- DATA_DIR:占用1bit 表示表空间是否是从默认的数据目录中获取的
- SHARED:占用1bit 是否为共享表空间
- TEMPORARY:占用1bit 是否为临时表空间
- ENCRYPTION:占用1bit 表空间是否加密
- UNUSED:占用18bit 没有使用到的比特位
- FRAG_N_USED:
- 占用4个字节
- FREE_FRAG链表中已使用的页数量,方便之后在链表中查找空闲的页
- Space Flags:
- 占用4个字节
- 表空间的一些占用存储空间比较小的属性
- List Base Node for FREE List:
- 占用16个字节
- FREE链表的基节点,方便定位到对应的 Free链表
- List Base Node for FREE_FRAG List:
- 占用16个字节
- FREE_FREG链表的基节点,方便定位到对应的 FREE_FREG链表
- List Base Node for FULL_FRAG List:
- 占用16个字节
- FULL_FREG链表的基节点,方便定位到对应的 FULL_FREG链表
- Next Unused Segment ID:
- 占用8个字节
- 当前表空间中下一个未使用的 Segment ID
- 表中每个索引都有两个段,一个段用来存储非叶子节点,一个段用来存储叶子节点,每个段都有自己独一无二的段号,每当创建新的索引时,就需要创建段号,但是为了保证段号唯一且不用再遍历一次,所以这个字段就会存储下一个新建的段号,每次都从这里取就可以了
- List Base Node for SEG_INODES_FULL List:
- 占用16个字节
- SEG_INODES_FULL链表的基节点
- 该链表中的INODE类型的页都已经被INODE Entry结构填充满了,没空闲空间存放额外的INODE Entry了。
- List Base Node for SEG_INODES_FREE List:
- 占用16个字节
- SEG_INODES_FREE链表的基节点
- 该链表中的INODE类型的页都已经仍有空闲空间来存放INODE Entry结构。
- Space ID:
XDES类型
- 每一个XDES Entry结构对应表空间的一个区,虽然一个XDES Entry结构只占用40字节,但是表空间的区数量很多,所以你不可能一个页就把所有的结构存储起来,所以想了个办法,在每个分组第一页放入这个分组256个区的XDES Entry结构,加起来也就是10240字节,妥妥的够用,结果如下图:
- 这里你会发现跟FSP_HDR类型很相似,因为XDES类型不是第一个页,所以不用存储表空间的相关属性,所以除了那些之外都是一样的
IBUF_BITMAP类型
- 记录了跟Change Buffer相关概念,后面再说,比较重要
INODE类型
- 看到这个名字,大家应该会想起来之前讲的INODE Entry结构,这个结构是跟段有关,老生常谈,每创建一个索引,就会创建两个段,为了方便管理每个段都会有INODE Entry结构,而这个类型的页就是为了存储INODE Entry结构存在的,结构如下图:
- File Header:文件头部 38字节 页的一些通用信息
- List Node for INODE Page List :通用链表节点 12字节 存储上一个INODE页和下一个INODE页的指针,因为可能一个INODE类型的页不足以存储所有的段对应的INODE Entry结构,所以就需要额外的INODE类型的页来存储这些结构。还是为了方便管理这些INODE类型的页,设计InnoDB的大佬们将这些INODE类型的页串联成两个不同的链表SEG_INODES_FULL链表 和 SEG_INODES_FREE链表,这两个结构在 FSP_HDR类型 页 的 File Space Header 中,所以我们可以说一下创建一个段的过程:
- 创建一个INODE Entry结构与之对应
- 先看看SEG_INODES_FREE链表是否为空,如果不为空,直接从该链表中获取一个节点,也就相当于获取到一个仍有空闲空间的INODE类型的页,然后把该INODE Entry结构防到该页中。当该页中无剩余空间时,就把该页放到SEG_INODES_FULL链表中。
- 如果SEG_INODES_FREE链表为空,则需要从表空间的FREE_FRAG链表中申请一个页,修改该页的类型为INODE,把该页放到SEG_INODES_FREE链表中,与此同时把该INODE Entry结构放入该页。
- INODE Entry:段描述信息 16128字节 每个INODE Entry结构占用192字节,一个页里可以存储85个这样的结构。
- Empty Space:尚未使用空间 6字节 用于页结构的填充,没什么实际意义
- File Trailer:文件尾部 8字节 校验页是否完整
Segment Header 结构的运用
- 一个索引对应两个段,一个段存储的是非叶子节点,一个段存储的是叶子节点,每个段都会对应一个INODE ENTRY结构,但是我们怎么知道 那个段对应哪个 INODE ENTRY 结构呢?
- 这个时候就需要有一个结构去对应这些信息,就是 Segment Header 结构
- Space ID of the INODE Entry :占用4字节 INODE Entry结构所在的表空间ID
- Page Number of the INODE Entry :占用4字节 INODE Entry结构所在的页页号
- Byte Offset of the INODE Ent:占用2字节 INODE Entry结构在该页中的偏移量
- 有了这个结构就号定位了,不知道还记不记得,当页的类型是INDEX 也就是索引的时候,页会有一个PAGE HEADER部分,里面有两个字段:
- PAGE_BTR_SEG_LEAF:占用10字节 B+树叶子段的头部信息,仅在B+树的根页定义
- PAGE_BTR_SEG_TOP:占用10字节 B+树非叶子段的头部信息,仅在B+树的根页定义
- 这两个字段就是存储上面说的 Segment Header 结构,这样我们就能知道这个页是属于哪个段的