mysql数据页结构
数据页结构
mysql默认每个数据页为16KB,InnoDB引擎的Compact行记录结构由以下字段组成:
变长字段长度列表 | null值列表 | 记录头信息 | RowID | 事务ID | 回滚指针 | 列数据1 | 列数据N |
---|---|---|---|---|---|---|---|
不定长 | 不定长 | 5字节 | 不定长 | 6字节 | 7字节 | 不定长 | 不定长 |
变长字段长度列表
对于非固定长度的字段类型,例如varchar
、text
、blob
、多字节编码的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后的值为00000011
即0x03
。如果表中允许为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 | CREATE TABLE `user_info` ( |
查看mysq目录下的文件user_info.ibd
,通过命令hexdump -Cv user_info.ibd > user_info.txt
或者使用软件例如UltraEdit
查看二进制数据。
普通记录
0x0000C078
位置
1 | 08 变长字段address的长度为8 |
最大/最小记录
数据页的每行数据以单向链表的形式连接,因此需要有链表头和链表尾。最小记录就是该单向链表的链表头,最大记录则是该单向记录的链表尾,且两者的记录位置固定不变。
最小记录的位置为0x0000C063
,其值固定为infimum
。
1 | 01 00 02 00 1D 记录头信息(5字节) |
最大记录的位置为0x0000C070
,其值固定为supremum
。
1 | 02 00 0B 00 00 记录头信息(5字节) |
Page Directory
Page Directory
中存放了当前数据页中部分数据的相对位置,即Page Directory
是当前数据页数据的目录,mysql通过Page Directory
使用二分法减少查询时间。
Page Directory
中每个相对位置占2个字节长度,且相对位置从0x0000FFF7
倒序排序。其中至少有最小记录和最大记录的相对位置,如下图:
00 63
表示 0x0000C063
,00 70
表示 0x0000C070
。
当表行数据较多时(创建了35行数据),Page Directory
会每4~8条记录存一个相对位置,例如下图:
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
使用二分法快速找到需要查找的行记录。
删除表(为了清空文件防止删除的记录干扰)后重新创建表和数据:
1 | CREATE TABLE `user_info` ( |
最小记录:
1 | 01 00 02 00 44 |
最大记录:
1 | 04 00 0B 00 00 |
ID为3的记录:
1 | 08 变长字段address的长度为8 |
ID为1的记录:
1 | 05 变长字段name的长度为5 |
ID为2的记录:
1 | 07 变长字段address的长度为7 |
Page Directory
最小记录的记录头信息最后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
,即最大记录的记录位置;