9.1 回忆一些旧知识
9.1.1 页面类型
类型名称 | 十六进制 | 描述 |
---|---|---|
FIL_PAGE_TYPE_ALLOCATED | 0x0000 | 最新分配,还没使用 |
FIL_PAGE_UNDO_LOG | 0x0002 | Undo日志页 |
FIL_PAGE_INODE | 0x0003 | 段信息节点 |
FIL_PAGE_IBUF_FREE_LIST | 0x0004 | Insert Buffer空闲列表 |
FIL_PAGE_IBUF_BITMAP | 0x0005 | Insert Buffer位图 |
FIL_PAGE_TYPE_SYS | 0x0006 | 系统页 |
FIL_PAGE_TYPE_TRX_SYS | 0x0007 | 事务系统数据 |
FIL_PAGE_TYPE_FSP_HDR | 0x0008 | 表空间头部信息 |
FIL_PAGE_TYPE_XDES | 0x0009 | 扩展描述页 |
FIL_PAGE_TYPE_BLOB | 0x000A | BLOB页 |
FIL_PAGE_INDEX | 0x45BF | 索引页,也就是我们所说的数据页 |
9.1.2 页面通用部分
File Header:记录页面的一些通用信息。
名称 | 占用空间 | 描述 |
---|---|---|
FIL_PAGE_SPACE_OR_CHKSUM | 4 字节 | 页的校验和(checksum值) |
FIL_PAGE_OFFSET | 4 字节 | 页号 |
FIL_PAGE_PREV | 4 字节 | 上一个页的页号 |
FIL_PAGE_NEXT | 4 字节 | 下一个页的页号 |
FIL_PAGE_LSN | 8 字节 | 页面被最后修改时对应的日志序列位置(英文名是:Log SequenceNumber) |
FIL_PAGE_TYPE | 2 字节 | 该页的类型 |
FIL_PAGE_FILE_FLUSH_LSN | 8 字节 | 仅在系统表空间的一个页中定义,代表文件至少被刷新到了对应的LSN值 |
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID | 4 字节 | 页属于哪个表空间 |
File Trailer:校验页是否完整,保证从内存到磁盘刷新时内容的一致性。
- 表空间中每个页都对应着一个页号,也就是
FIL_PAGE_OFFSET
,它由4字节组成,所以一个表空间最多可以有2^32^个页,按每个页16KB来算,一个表空间最多支持64TB的数据。 - 某些类型的页可以组成链表,链表中的页可以不按照物理顺序存储。
9.2 独立表空间结构
9.2.1 区(extent)的概念
连续的64个页就是一个区,即一个区占用1MB空间。
每256个区被划分成一组。
从上图可知:
-
第一个组最开始的3个页面类型是固定的,分别是:
-
FSP_HDR,登记整个表空间的一些整体属性以及本组所有的区。
-
IBU_BITMAP,存储本组所有页面关于
INSERT BUFFER
的信息。 -
INODE,存储了许多称为
NODE
的数据结构。
-
-
其余各组的前两个页面类型是固定的,分别是:
- XDES类型,extent descriptor,登记本组256个区的属性。
- IBUF_BITMAP,同上。
9.2.2 段(segment)的概念
为了防止出现相邻的两个区在磁盘相距太远的问题造成大量随机I/O影响性能的问题发生,而引入了区
的概念。有了区
以后,为某个索引分配磁盘空间时,就以区为单位,可以消除很多随机I/O。
存放叶子节点的区的集合是一个段;存储非叶子节点的区的集合是另外一个段。也就是说每个索引都会生成两个段,一个叶子节点段,一个非叶子节点段。
碎片(fragment)区:在一个碎片区中,并不是所有的页都为为了存储同一个段的数据而存在的,而是碎片区中的页可以用于不同的段。碎片区直属于表空间,并不属于任何一个段。这时为某个段分配存储空间的策略是:
- 在刚开始向表中插入数据的时候,段是从某个碎片区以单个页面为单位来分配存储空间的。
- 当某个段已经占用了32个碎片区页面之后,就会以完整的区为单位来分配存储空间。
9.2.3 区的分类
状态名 | 含义 | 说明 |
---|---|---|
FREE | 空闲的区 | 现在还没有用到这个区中的任何页面 |
FREE_FRAG | 有剩余空间的碎片区 | 碎片区中还有可用的页面 |
FULL_FRAG | 没有剩余空间的碎片区 | 碎片区中所有页面都被使用,没有空闲页面 |
FAEG | 附属于某个段的区 | 除了叶子节点和非叶子节点段,InnoDB还会另外定义一些特殊的段。 |
处于FREE、FREE_FRAG以及FULL_FRAG三种状态的区直属于表空间;而处理FAEG状态的区是附属于某个段的。
- Segment ID(8字节),表示该区所属段的ID。
- List Node(12字节),将若干个XDES Entry结构串联成一个链表。
- State(4字节),表明区的状态。
- Page State Bitmap(16字节),128位,区中每个页2位,第1位表示该页是否空闲,第2位还没有用。
frag:v. (用爆炸装置)蓄意杀伤,n. 碎片弹;手榴弹 fragment:n. 碎片;片段;残存部分,v. (使)破碎,分裂 fragmentation:n. 破碎,分裂;分段储存 内存碎片:memory fragmentation
9.2.3.1 XDES(Extent Descriptor) Entry链表
对于表空间:
- 把状态为FREE的区对应的XDES Entry结构连接成一个链表,称为FREE链表
- 把状态为FREE_FRAG的区对应的XDES Entry结构连接成一个链表,称为FREE_FRAG链表
- 把状态为FULL_FRAG的区对应的XDES Entry结构连接成一个链表,称为FULL_FRAG链表
对于具体的段:
- FREE链表:同一个段中,所有页面都是空闲的区对应的XDES Entry结构会被加到这个链表。
- NOT_FULL链表:同一个段中,仍有空闲空间的区对应的XDES Entry结构会被加到这个链表。
- FULL链表:同一个段中,已经没有空闲空间的区对应的XDES Entry结构会被加到这个链表。
举例:
CREATE TABLE t (
c1 INT NOT NULL AUTO_INCREMENT,
c2 VARCHAR(100),
c3 VARCHAR(100),
PRIMARY KEY (c1),
KEY idx_c2 (c2)
)ENGINE=InnoDB;
表t有两个索引,所有共有4个段,每个段维护上述3个链表,总共12个链表。再加上直属表空间的3个链表,整个独立表空间共需要维护15个链表。
9.2.3.2 链表基节点
- List Length,表示该链表一共有多少节点
- First Node Page Number 和 First Node Offset,表示该链表的头节点在表空间中的位置
- Last Node Page Number 和 Last Node Offset,表示该链表的尾节点在表空间中的位置
我们把某个链表对应的List Base Node结构放在表空间中固定的位置,使用起来就很方便。
9.2.3.3 链表小结
- 表空间由若干个区组成
- 每个区对应一个 XDES(Extent Descriptor) Entry结构
- 直属于表空间的区对应的 XDES Entry 结构可以分为 FREE、FREE_FRAG 和 FULL_FRAG 3个链表
- 每个段中的区对应的 XDES Entry 结构可以分为 FREE、NOT_FULL 和 FULL 3个链表
- 每个链表对应一个 List Base Node 结构,记录的链表的头、尾、长度等信息,它们被储存在固定位置,便于使用。
9.2.4 段的结构
段是一个逻辑上的概念,由若干个零散的页面以及一些完整的区组成。使用 INODE Entry 来记录段中的属性。
- Segemnt ID,指这个 INODE Entry 结构对应的段的编号(ID)
- NOT_FULL_N_USED,指在 NOT_FULL 链表中已经使用了多少个页面。下次从 NOT_FULL 链表分配空闲页面时可以直接根据这个字段的值定位到。
- 3个 List Base Node,分别对应段的 FREE、NOT_FULL 和 FULL 三个链表。
- Magic Number,标记这个 INODE Entry 是否已经被初始化了。97937874 表明已经初始化。
- Fragment Array Entry,每个 Fragment Array Entry 结构对应一个零散的页面。
9.2.5 各类型页面详细情况
9.2.5.1 FSP_HDR 类型
名称 | 中文名 | 占用空间大小 | 简单描述 |
---|---|---|---|
File Header | 文件头部 | 38字节 | 页的一些通用信息 |
File Space Header | 表空间头部 | 112字节 | 表空间的一些整体属性信息 |
XDES Entry | 区描述信息 | 10240字节 | 存储本组256个区对应的属性信息 |
Empty Space | 尚未使用空间 | 5986字节 | 用于页结构的填充,没有实际意义 |
File Trailer | 文件尾部 | 8字节 | 校验页是否完整 |
File Space Header部分
名称 | 大小(字节) | 描述 |
---|---|---|
Space ID | 4 | 表空间的 ID |
Not Used | 4 | 未被使用,忽略 |
Size | 4 | 当前表空间拥有的页面数 |
FREE Limit | 4 | 尚未被初始化的最小页号,大于或等于这个页号的区对应的 XDES Entry 结构都没有被加入 FREE 链表 |
Space Flags | 4 | 表空间的一些占用存储空间比较小的属性 |
FRAG_N_USED | 4 | FREE_FRAG 链表中已使用的页面数量 |
List Base Node for FREE List | 16 | FREE 链表的基节点 |
List Base Node for FREE_FRAG List | 16 | FULL_FREG 链表的基节点 |
List Base Node for FULL_FRAG List | 16 | FULL_FREG 链表的基节点 |
Next Unused Segemnt ID | 8 | 当前表空间中下一个未使用的 Segemnt ID |
List Base Node for SEG_INODES_FULL List | 16 | SEG_INODES_FULL 链表的基节点 |
List Base Node for SEG_INODES_FREE List | 16 | SEG_INODES_FREE 链表的基节点 |
9.2.5.2 XDES 类型
9.2.5.3 IBUF_BITMAP 类型
Change Buffer
9.2.5.4 INODE 类型
名称 | 中文名 | 大小(字节) | 简单描述 |
---|---|---|---|
File Header | 文件头部 | 38 | 页的通用信息 |
List Node for INODE Page List | 通用链表节点 | 12 | 存储上一个 INODE 页面和下一个 INODE 页面的指针 |
INODE Entry | 段描述信息 | 16128 | |
Empty Space | 尚未使用空间 | 6 | 用于页结构的填充,无实际意义 |
File Trailer | 文件尾部 | 8 | 校验页是否完整 |
9.2.6 Segment Header结构的运用
INDEX 类型页有一个 Page Header 部分,其中
名称 | 占用空间(byte) | 描述 |
---|---|---|
PAGE_BTR_SEG_LEAF | 10 | B+树叶子段的头部信息,仅在B+树的Root页定义 |
PAGE_BTR_SEG_TOP | 10 | B+树非叶子段的头部信息,仅在B+树的Root页定义 |
这二位都对应了一个叫作 Segemnt Header 的结构:
PAGE_BTR_SEG_LEAF 记录着叶子节点段对应的 INODE Entry 结构的地址是哪个表空间的哪个页面的哪个偏移量,PAGE_BTR_SEG_TOP 记录着非叶子节点段对应的 INODE Entry 结构的地址是哪个表空间的哪 个页面的哪个偏移量。这样子索引和其对应的段的关系就建立起来了。
9.2.7 真实表空间对应的文件大小
即.idb文件的大小
9.3 系统表空间
9.3.1 系统表空间的整体结构
9.3.1.1 InnoDB 数据字典
Data Dictionary Header 页面
information_schema 系统数据库
USE information_schema;
SHOW TABLES LIKE 'innodb_sys%';
9.3.2 总结图
9.4 总结
- 不同类型的页面有不同的功能,但都有 File Header 和 File Trailer 的通用结构。
- 表空间被划分为许多连续的区,每个区默认64个页,每256个区为一组,每组最开始几个页面的类型是固定的。
- 段是一个逻辑上的概念,是某些零散的页面以及一些完整的区的集合。
- 每个区对应一个 XDES Entry 结构。
- 每个段对应一个 INODE Entry 结构。
- 表空间中第一个页面的类型是 FSP_HDR,它存储了表空间的一些整体属性以及第一个组内256个区对应的 XDES Entry 结构。
- 除了第一个组以外,其余组的第一个页面的类型为 XDES 。
- 每个组的第二个页面的类型为 IBUF_BITMAP。
- Segemnt Header 结构占用10字节,为了定位到具体的 INODE Entry 结构。
- InnoDB 提供了一系列系统表来描述元数据。