【MySQL】InnoDB存储引擎中的页
目录
- 1、背景
- 2、页的组成
- 3、各部分讲解
- 【1】文件头部
- 【2】页头部
- 【3】最小记录和最大记录
- 【4】行记录
- 【5】空闲空间
- 【6】页目录
- 【7】文件尾部
- 4、总结
1、背景
mysql中存储数据是存储引擎干的事,存储引擎存储数据的基本单位是页,我们往数据库插入表中的一条条记录就是存储在页上的,今天我们就来熟悉一下页上面有哪些内容。
2、页的组成
页由7部分组成,先大概字面意思理解一下,后面再各部分详细讲解,组成图如下:
表格解释如下:
名称 | 字节大小 | 描述 |
---|---|---|
文件头部 | 38 | 页的通用信息 |
页头部 | 56 | 页的专有信息 |
最小记录和最大记录 | 26 | 2个固定的行记录 |
行记录 | 不固定 | 存放用户数据 |
空闲空间 | 不固定 | 未使用存放用户数据的空间 |
页目录 | 不固定 | 存放槽 |
文件尾部 | 8 | 校验页面是否完整 |
3、各部分讲解
【1】文件头部
文件头部38个字节由8个部分组成,看表理解:
名称 | 字节大小 | 描述 |
---|---|---|
FIL_PAGE_SPACE_OR_CHKSUM | 4 | 表空间id或校验和 |
FIL_PAGE_OFFSET | 4 | 页号,当前页的唯一标识 |
FIL_PAGE_PREV | 4 | 上一个页号唯一标识 |
FIL_PAGE_NEXT | 4 | 下一个页号唯一标识 |
FIL_PAGE_LSN | 8 | 该页最后一次修改对应的redo日志序号 |
FIL_PAGE_TYPE | 2 | 页类型 |
FIL_PAGE_FILE_FLUSH_LSH | 8 | 该页被刷到磁盘的redo日志序号 |
FIL_PAGE_ARCH_LOG_NO_OR_SPACE_ID | 4 | 归档日志编号或页属于哪个表空间 |
1、本文是在FIL_PAGE_TYPE为数据页的基础上进行讲解
2、FIL_PAGE_SPACE_OR_CHKSUM对于数据页的类型含义为校验和,用来和文件尾部的校验和进行对比
【2】页头部
页头部的56个字节由14个部分组成,看表理解:
名称 | 字节大小 | 描述 |
---|---|---|
PAGE_N_DIR_SLOTS | 2 | 页目录中存储槽的数量 |
PAGE_HEAP_TOP | 2 | 页中空闲空间地址 |
PAGE_N_HEAP | 2 | 未标记删除用户记录 + 已标记删除用户记录 + 最小记录 + 最大记录 |
PAGE_FREE | 2 | 第1条标记为已删除的用户记录地址 |
PAGE_GARBAGE | 2 | 所有标记为已删除记录占用的字节数 |
PAGE_LAST_INSERT | 2 | 最后一条用户记录插入位置 |
PAGE_DIRECTION | 2 | 最后一条用户记录插入方向,主键/unique健/隐藏列唯一键比上一条大就为右,反之为左 |
PAGE_N_RECS | 2 | 未标记为删除的用户记录数 |
PAGE_MAX_TRX_ID | 8 | 修改当前页的最新事务id |
PAGE_LEVEL | 2 | 当前页在B+树中的层级 |
PAGE_INDEX_ID | 8 | 当前页属于的索引ID |
PAGE_BTR_SEG_LEAF | 10 | B+树叶子段的头部信息 |
PAGE_BTR_SEG_TOP | 10 | B+树非叶子段的头部信息 |
【3】最小记录和最大记录
最小记录和最大记录2条记录是页中固定就存在的,最小记录和最大记录都是由5字节头部信息+8字节数据组成,所以它们总共占用空间26字节,最小记录数据部分固定为’infimum’,最大记录数据部分固定为’supremum’, 在讲它之前我们先了解一下Compact行格式中头部信息用5字节来存储的行记录信息,其组成部分如下:
名称 | 位大小 | 描述 |
---|---|---|
预留位 | 1 | 未使用 |
预留位 | 1 | 未使用 |
delete_mask | 1 | 0-记录未删除,1-记录已删除 |
min_rec_mask | 1 | B+树非叶子节点中的最小记录 |
n_owned | 4 | 当前记录在记录组里拥有记录数 |
heap_no | 13 | 当前记录在记录堆中的位置 |
record_type | 3 | 记录类型,0-普通记录,1-B+数非叶子节点记录,2-最小记录,3-最大记录 |
next_record | 16 | 指向下一条未被删除的记录 |
当页中没有任何用户记录时,最小记录和最大记录可以如图表示:
最小记录中的n_owned为1表示以最小记录结尾的分组记录中只有一条记录,这条记录就是最小记录本身,最小记录的next_record为13代表最小记录数据部分与下一条数据部分的偏移量,所以上图箭头指向最大记录的固定数据部分,13字节大小是这样计算的: 最小记录固定数据部分(8) + 最大记录头部信息(5)
next_record之所以指向下一条记录的数据部分是因为:对于用户记录,指向位置往左的头部信息中还有逆序的长度列表、逆序的是否为NULL列表,往右就是数据部分。
最大记录中的n_owned为1表示以最大记录结尾的分组记录中只有一条记录,这条记录就是最大记录本身,最大记录的next_record为0代表没有下一条记录,它就是最后一条记录。
n_owned数有这样的定义
1、最小记录所在分组记录数最多只能有1条。
2、最大记录所在分组记录数范围为1~8条。
3、用户记录所在分组记录数范围为4~8条。
【4】行记录
行记录讲解需要根据数据来进行理解了,我们先创建一张表如下:
CREATE TABLE test
(
id INT AUTO_INCREMENT PRIMARY KEY,
str VARCHAR(255) NOT NULL DEFAULT ''
) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4;
插入3条记录:
INSERT INTO test (str)
VALUES ('AAA'),
('BBB'),
('CCC');
查看记录:
mysql [xxx]> select * from test;
+----+-----+
| id | str |
+----+-----+
| 1 | AAA |
| 2 | BBB |
| 3 | CCC |
+----+-----+
3 rows in set (0.001 sec)
此时插入的3条记录和最小最大记录可以如图表示:
可以看到所有记录通过nex_record的偏移量组成了一个链表,删除其中某条记录,就会把该条记录的delete_mask设置为1,next_record设置为0,但是记录依然存在,只是标记为了删除,这是为了后面插入新数据时可以对这部分空间进行复用,如图:
因为是第1条标记删除的记录,也会在页头部记录这条记录的地址,如果有多条删除记录,它们之间也会组成一个链表。
【5】空闲空间
空闲空间就是给行记录使用的,行记录空间增加,空闲空间就减少。
【6】页目录
页目录就是用来存储槽的,槽就是一组用户记录中,相对页起始位置偏移量最大的一条记录, 这条记录数据部分相对页起始位置的偏移量就作为槽,槽对应的记录中的n_owned属性就代表这条记录对应的用户组内有多少条记录,槽与记录关系可以如图表示:
最大记录看起来虽然是在最后面,起始它是和最小记录挨着的,所以后面记录增加导致分组产生新增槽时相对页起始位置的偏移量要比槽1大。
在讲最小记录和最大记录时讲过分组记录的约束,根据这个约束行记录分组产生规则如下:
1、新增记录放到比插入记录主键值大且主键值相隔最近的行记录对应的槽里,并将当前槽对应行记录的n_owned属性+1,表示该槽对应的记录组的记录多了1个。
2、当记录组的记录数变为9个时,会将这9条记录分为2个组,一组4个一组5个,对于4条记录的分组会将里面相对页起始位置最大的值的行记录的数据部分偏移量(相对页起始位置)设置为新的槽。
查找记录的规则如下:
1、通过二分法找到对应的槽。
2、根据行记录的next_record属性找到对应的行。
【7】文件尾部
文件尾部大小为8个字节,前4个字节代表页的校验和,和文件头部的校验和相等就代表是一个完成的页,后4个字节代表最后一次修改当前页是对应的redo日志序号,这个以后再讲。
4、总结
本篇文章讲解了数据页的组成,页是存储数据的基本单位;每个页的文件头部都会有一个上下页号,组成了一个双向链表;页中每条行记录的next_record组成一个单项链表;InnoDB会把多条记录分为一个组,组里的最大记录的数据部分相对于页面开头的偏移量就设置为槽,查找记录时通过二分法查找所在的槽,再通过行记录的next_record属性找到对应的记录。