在上一篇中,学习了页的结构和行记录信息在页中的与伪记录的关系。这一篇中先验证一下伪记录与行记录的关系。下面开始~
CREATE TABLE `compact_record_page` (
`col0` int(11) NOT NULL,
`col1` varchar(10) DEFAULT NULL,
`col2` char(5) DEFAULT NULL,
`col3` int(11) DEFAULT NULL,
`col4` varchar(5) DEFAULT NULL,
`col5` varchar(15) NOT NULL,
PRIMARY KEY (`col0`)
) ENGINE=InnoDB DEFAULT CHARSET=ascii ROW_FORMAT=COMPACT
INSERT INTO mysqlstudy.compact_record_page (col0, col1, col2, col3, col4, col5) VALUES (1, 'aa', 'aaa', 10, null, 'aaa');
INSERT INTO mysqlstudy.compact_record_page (col0, col1, col2, col3, col4, col5) VALUES (2, 'bb', 'bbb', 10, null, 'bbb');
INSERT INTO mysqlstudy.compact_record_page (col0, col1, col2, col3, col4, col5) VALUES (3, 'cc', 'ccc', 10, null, 'ccc');
INSERT INTO mysqlstudy.compact_record_page (col0, col1, col2, col3, col4, col5) VALUES (4, 'dd', 'ddd', 10, null, 'ddd');
根据Infimum的69 6e 66 69 6d 75 6d 00 往前找5个字节构成Infimum伪记录信息得到以下16进制字节信息
总共13个字节 其中5个字节的头信息 还有8个字节表示Infimum单词的ascii字符数组 最后一个00 表示补位
01 00 02 00 1d
69 6e66 696d 756d 00
基于下图进行伪记录头信息解析
01
对应的2进制 0000 0001
其中第一个预留位为0,第二个预留位为0,第三个delete_flag
为0,第四个min_rec_flag
为0,第五位到第8位共四个比特位表示n_owned,且数值为1,意味这Infimum是分组里的老大,且组内记录只有一条。那说明Infimum自成一组。
00 02
对应的2进制 0000 0000 0000 0010
取前13个比特位 0000 0000 0000 0
表示heap_no
的值。说明Infimum伪记录的在页内行记录的相对位置为0。取后3个比特位 010
表示record_type
,值为2,刚好与上图Infimum记录的类型相符。
00 1d
对应的2进制为 0000 0000 0001 1101
,取这16个比特位标识next_record,其值为29。表示当前位置到下一条记录真实数据的位置为29
69 6e 66 69 6d 75 6d 00
总共8个字节,最后一个字节是补位。前七个字节分表表示Infimum中的每个字符。
从Infimum记录往后找13个字节得到Supremum
五个字节的头信息 + 8个字节的Supremum单词ascii字符
05 00 0b 00 00 73 75 70 72 65 6d 75 6d
05
对应的二进制为 0000 0101
,形同Infimum分析,这里只有n_owned
不同,且数值为5,意味着Supremum是分组内的大佬,且管理着四条插入的用户记录,所以组内共5条记录。
00 0b
对应的二进制为 0000 0000 0000 1011
,取前13位比特位作为head_no
,对应的值为1,表示在页内的相对位置为1。 取后3个比特位作为record_type
,对应值为3,刚好与Supremum记录类型的值一致。
00 00
对应的二进制为 0000 0000 0000 0000
,取这16个比特位作为next_record,表示当前记录的真是数据到下一条记录的真是数据的距离为0。也就是没有下一条记录了。
第一条记录中,所有字符串值都为a,a对应的ascii码为61。
根据Infimum中的next_record=29,我们需要往后找29个字节。
其中Infimum的内容占8个字节,Supremum占13个字节,那么还剩8个字节。
那么就是可变字符长度列表+null值列表+固定头信息
INSERT INTO mysqlstudy.compact_record_page
(col0, col1, col2, col3, col4, col5) VALUES
(1, 'aa', 'aaa', 10, null, 'aaa');
03 col5可变字符长度
02 col1 可变字符长度
08 的2进制为 0000 0100 。当前总共有四个可为null的列,从后往前分别是 col4 col3 col2 col1,
基于null值列表的倒序排列规则,从后往前col3的值对应于1的位置,也就是标识为1了。至于前面4个0,则是补位。
00 00 10 00 27 固定头信息(稍后会在下面详细解析)
80 00 00 01 00 00 事务ID
00 26 7c 66 d6 00 00 回滚指针
01 37 01 10 主键内容(特殊格式,后面讲解)
61 61 col1的值
61 61 61 20 20 col2的值 固定长度字符,实际长度不足时,在后面补空格(ascii 20)
80 00 00 0a col3的值 int型表示方法
61 61 61 col5的值
头信息解析
头信息的16进制格式:
00 00 10 00 27
头信息的二进制格式:
0000 0000 0000 0000 0001 0 000 0000 0000 0010 0111
头信息同Infimum和Supremum进行解析
预留位 0
预留位 0
delete_flag 0
min_rec_flag 0
n_ownd 0000
这是跟Infimum和Supremum的地方,用户记录可以作为组老大也可以不作为组老大,这个是跟一个页类用户记录数和页切分有关的,会在后面的内容进行讲解。
head_no 0000 0000 0001 0
转为10进制数值为2,表示在堆内位置为2
record_type 000
转为10进制数值为0,代表普通记录
next_record 0010 0111
转为10进制数值为39 ,下一条记录的真实数据起始位置为第39个字节
非真实数据长度:2(变长字段列表)+1(null值列表)+5(头信息) = 8个字节
真实数据总共占据 6(事务ID)+7(回滚指针) + 4(col0)+2(col1)+5(col2)+4(col3)+3(col5) = 31个字节
Infimum的next_record计算为:Infimum的真实数据8字节+Supremum整个记录的13字节+第一条记录的非真实数据长度8字节 = 8+13+8 = 29 字节
刚好与Infimum的next_record 一致
03 col5 可变字符长度
02 col1 可变字符长度
08 null值列表 (同第一条记录)
00 00 18 00 27 固定头部信息(稍后下面进行解析)
80 00 00 02 00 00 事务ID
00 26 7c 67 d7 00 00 回滚指针
01 38 01 10 主键内容(特殊格式,后面讲解)
62 62 col1的值
62 62 62 20 20 col2的值
80 00 00 0a col3的值
62 62 62 vol5的值
解析头部信息
00 00 18 00 27
0000 0000 0000 0000 0001 1000 0000 0000 0010 0100
省略部分雷同头信息
head_no 0000 0000 0001 1
标识堆内位置为3
record_type 000
标识为普通记录
next_record 0010 0100
标识下一条记录的真实数据起始位置相对于当前记录真实数据起始位置的距离为39
第二条记录长度计算
非真实数据长度:2(变长字段列表)+1(null值列表)+5(头信息) = 8个字节
真实数据总共占据 6(事务ID)+7(回滚指针) + 4(col0)+2(col1)+5(col2)+4(col3)+3(col5) = 31个字节
第一条记录的真实数据长度+第二条记录的非真实数据长度 = 31+8 = 39
由此验证了关于next_record 的相关规则。
根据第一条和第二条记录,这里便不再进行解析,直接解析头信息的区别
03 02 可变长度列表
08 null值列表
00 00 20 00 27 头信息
80 00 00 03 00 00 事务ID
00 26 7c 6c da 00 00 回滚指针
01 3a 01 10 主键
6363 col1
63 63 63 20 20 col2
80 00 00 0a col3
63 63 63 col5
第三条和前面两条基本一致所以会省略一些信息。只列出不同点
头信息解析:
00 00 20 00 27
0000 0000 0000 0000 0010 0000 0000 0000 0010 0111
这里只有head_no 不一样为 0000 0000 0010 0
数值为4 ,表示在页内相对记录的第四条
根据第一条和第二条记录,这里便不再进行解析,直接解析头信息的区别
03 02
08
00 00 28 ff 7b
80 00 00 04 00 00
00 26 7c 6d db 00 00
01 b2 01 10
64 64
64 64 64 20 20
80 00 00 0a
64 64 64
这里由于是最后一条真实用户记录,所以头信息会跟前面的记录有所不同
解析头信息:
00 00 28 ff 7b
0000 0000 0000 0000 0010 1000 1111 1111 0111 1011
head_no 0000 0000 0010 1
对应数值5 堆内为5
record_type 000
标识普通记录
next_record 1111 1111 0111 1011
第一位为1时,我们通常会想到整型的负数在计算机中存储的形式为补码,这里涉及到原码,反码 ,补码。
我们来计算一下
补码:
1111 1111 0111 1011
反码:负数的补码-1 得到反码
1111 1111 0111 1010
原码:除符号位以外进行取反
1000 0000 1000 0101
原码的十进制为133
往前找到Supremum的真实数据起始位置:
第四条非真实:8字节
第三条:39 字节
第二条:39 字节
第一条: 39 字节
Supremum真实数据字节:8
共计133字节
而最后一条next_record实际值为133字节
这里验证了关于伪记录的位置和用户记录的关系。其中对行结构的读取方式和next_record的计算方式进行了深入验证。下一篇将验证一下删除一条记录后,伪记录与行记录将发生什么变化~