mysql数据页结构

数据页结构

mysql默认每个数据页为16KB,InnoDB引擎的Compact行记录结构由以下字段组成:

变长字段长度列表 null值列表 记录头信息 RowID 事务ID 回滚指针 列数据1 列数据N
不定长 不定长 5字节 不定长 6字节 7字节 不定长 不定长

变长字段长度列表

对于非固定长度的字段类型,例如varchartextblob、多字节编码的char等,通过变长字段长度列表记录当前行记录的对应字段值的长度(列顺序倒序存放)。

列名 值长度(10进制) 值长度(16进制)
a 'aaa' 3 0x03
b 'b' 1 0x01
c 'ccccc' 5 0x05

则示例的变长字段长度列表的值为05 01 03

null值列表

如果记录的列允许为null,则在该值中记录当前行中值为null的字段(列顺序倒序存放)。

列名 允许为null 是否为null 值(2进制)
a true true 1
b false - -
c true true 1
d true false 0

则示例的null值列表的值为011(二进制),又由于该值必须为整数字节,所以高位补0后的值为000000110x03。如果表中允许为null的列超过8个,则null值列表的值长度大于1字节。

记录头信息

记录头占固定长度5字节:

名称 大小(bit) 描述
() 1 未知
() 1 未知
deleted_flag 1 该行是否已被删除
min_rec_flag 1 为1,如果该记录是预先定义为最小的记录
n_owned 4 该记录拥有的记录数
heap_no 13 索引堆中该条记录的排序记录
record_tye 3 记录类型,000表示普通,001表示B+树节点指针,010表示Infimum,011表示Supremum,1xx表示保留
next_record 16 页中下一条记录的相对位置

案例

1
2
3
4
5
6
7
8
9
CREATE TABLE `user_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(11) NOT NULL,
`age` int(11) DEFAULT NULL,
`address` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

insert into user_info (id,name,age,address) values (1,'tom',18,'shanghai');

查看mysq目录下的文件user_info.ibd,通过命令hexdump -Cv user_info.ibd > user_info.txt或者使用软件例如UltraEdit查看二进制数据。

普通记录

0x0000C078位置

image

1
2
3
4
5
6
7
8
9
10
08                      变长字段address的长度为8
03 变长字段name的长度为3
00 null值字段列表
00 00 10 FF F0 记录头信息(5字节)
80 00 00 01 RowID:主键id的值为1
00 00 00 1D 4B 93 事务ID(6字节)
E7 00 00 80 31 01 10 回滚指针(7字节)
74 6F 6D 字段name的值:tom
80 00 00 12 字段age的值:18
73 68 61 6E 67 68 61 69 字段address的值:shanghai

最大/最小记录

数据页的每行数据以单向链表的形式连接,因此需要有链表头和链表尾。最小记录就是该单向链表的链表头,最大记录则是该单向记录的链表尾,且两者的记录位置固定不变。

最小记录的位置为0x0000C063,其值固定为infimum

image

1
2
01 00 02 00 1D          记录头信息(5字节)
69 6E 66 69 6D 75 6D 最小记录(固定值infimum)

最大记录的位置为0x0000C070,其值固定为supremum

image

1
2
02 00 0B 00 00          记录头信息(5字节)
73 75 70 72 65 6D 75 6D 最大记录(固定值supremum)

Page Directory

Page Directory中存放了当前数据页中部分数据的相对位置,即
Page Directory是当前数据页数据的目录,mysql通过Page Directory使用二分法减少查询时间。

Page Directory中每个相对位置占2个字节长度,且相对位置从0x0000FFF7倒序排序。其中至少有最小记录和最大记录的相对位置,如下图:

00 63 表示 0x0000C06300 70 表示 0x0000C070

image

当表行数据较多时(创建了35行数据),Page Directory会每4~8条记录存一个相对位置,例如下图:

image

00 63 -> 0x0000C063 -> 最小记录

00 F8 -> 0x0000C0F8 -> ID为4的记录

01 98 -> 0x0000C198 -> ID为8的记录

02 38 -> 0x0000C238 -> ID为12的记录

02 D8 -> 0x0000C2D8 -> ID为16的记录

03 78 -> 0x0000C378 -> ID为20的记录

04 18 -> 0x0000C418 -> ID为24的记录

04 B8 -> 0x0000C4B8 -> ID为28的记录

00 70 -> 0x0000C070 -> 最大记录

数据链表

数据页中行记录按照主键值由小到大顺序串联成一个单链表,且单链表的链表头为最小记录,链表尾为最大记录。并且为了更快速地定位到指定的行记录,通过Page Directory实现目录的功能,借助Page Directory使用二分法快速找到需要查找的行记录。

image

删除表(为了清空文件防止删除的记录干扰)后重新创建表和数据:

1
2
3
4
5
6
7
8
9
10
11
CREATE TABLE `user_info` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(11) NOT NULL,
`age` int(11) DEFAULT NULL,
`address` varchar(100) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

insert into user_info (id,name,age,address) values (3,'tom',18,'shanghai');
insert into user_info (id,name,age,address) values (1,'peter',22,null);
insert into user_info (id,name,age,address) values (2,'wsen',21,'beijing');

image

最小记录:

1
2
01 00 02 00 44
69 6E 66 69 6D 75 6D 最小记录(固定值infimum)

最大记录:

1
2
04 00 0B 00 00
73 75 70 72 65 6D 75 6D 最大记录(固定值supremum)

ID为3的记录:

1
2
3
4
5
6
7
8
9
10
08                      变长字段address的长度为8
03 变长字段name的长度为3
00 null值字段列表
00 00 10 FF F0 记录头信息(5字节)
80 00 00 03 RowID:主键id的值为3
00 00 00 1D 4B 80
DB 00 00 80 2E 01 10
74 6F 6D 字段name的值:tom
80 00 00 12 字段age的值:18
73 68 61 6E 67 68 61 69 字段address的值:shanghai

ID为1的记录:

1
2
3
4
5
6
7
8
9
05                      变长字段name的长度为5
02 null值字段列表:0x02 -> 00000010即address为null
00 00 18 00 22 记录头信息(5字节)
80 00 00 01 RowID:主键id的值为1
00 00 00 1D 4B 81
DC 00 00 80 1B 01 10
70 65 74 65 72 字段name的值:peter
80 00 00 16 字段age的值:22
字段address的值:null

ID为2的记录:

1
2
3
4
5
6
7
8
9
10
07                      变长字段address的长度为7
04 变长字段name的长度为4
00 null值字段列表
00 00 20 FF B7 记录头信息(5字节)
80 00 00 02 RowID:主键id的值为2
00 00 00 1D 4B 86
DF 00 00 80 2F 01 10
77 73 65 6E 字段name的值:wsen
80 00 00 15 字段age的值:21
62 65 69 6A 69 6E 67 字段address的值:beijing

Page Directory

image

最小记录的记录头信息最后2字节00 44 -> 0x0000C063偏移0x0044 -> 0x0000C0A7,即ID为1的记录的id位置;

ID为1的记录的记录头信息最后2字节00 22 -> 0x0000C0A7偏移0x0022 -> 0x0000C0C9,即ID为2的记录的id位置;

ID为2的记录的记录头信息最后2字节FF B7 -> 0x0000C0C9偏移0xFFB7 -> 0x0000C080,即ID为3的记录的id位置;

ID为3的记录的记录头信息最后2字节FF F0 -> 0x0000C080偏移0xFFF0 -> 0x0000C070,即最大记录的记录位置;

>