教你如何使用UE观察InnoDB Page结构-爱可生

导读:

贴心tips:在阅读本文之前建议先快速阅读一下《InnoDB Page结构详解》

点击了解

创建表结构,插入三条数据

<code>root@mysqldb 11:01: [xucl]> show create table t\G*************************** 1. row *************************** Table: tCreate Table: CREATE TABLE `t` ( `id` int(11) NOT NULL AUTO_INCREMENT, `c1` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb41 row in set (0.00 sec)root@mysqldb 11:01: [xucl]> select * from t;+----+------+| id | c1 |+----+------+| 1 | a || 2 | NULL || 3 | a |+----+------+3 rows in set (0.00 sec)/<code>

用ue打开t.ibd文件,为什么用ue呢,我觉得ue是观察16进制文件最好的工具了,hexdump由于大小端的原因,看起来太费劲了。

page no=3是第一个数据页,那么我们从0000c000到00010000之间就是我们这个数据页所有的数据了。

整理一下得到如下:

<code> 0 1 2 3 4 5 6 7 8 9 a b c d e fc000 38 AD C1 F5 00 00 00 03 FF FF FF FF FF FF FF FFc010 00 00 00 11 4E 07 B2 CA 45 BF 00 00 00 00 00 00c020 00 00 00 00 04 D2 00 02 00 C1 80 05 00 00 00 00c030 00 AF 00 02 00 02 00 03 00 00 00 00 00 00 00 00c040 00 00 00 00 00 00 00 00 08 48 00 00 04 D2 00 00c050 00 02 00 F2 00 00 04 D2 00 00 00 02 00 32 01 00c060 02 00 1C 69 6E 66 69 6D 75 6D 00 02 00 0B 00 00c070 73 75 70 72 65 6D 75 6D 01 00 00 00 10 00 18 80c080 00 00 01 00 00 00 0C 81 E7 B5 00 00 00 05 01 10c090 61 01 00 00 18 00 18 80 00 00 02 00 00 00 0C 81c0a0 E8 B6 00 00 00 05 01 10 01 00 00 00 20 FF C1 80 c0b0 00 00 03 00 00 00 0C 81 E9 B7 00 00 00 05 01 10c0c0 61................................fff0 00 00 00 00 00 70 00 63 38 AD C1 F5 4E 07 B2 CA/<code>

先看File Header 38字节

38 AD C1 F5:4字节,表示数据页的checksum值00 00 00 03:4字节,表示页的偏移量,这个页也就是page no = 3的页FF FF FF FF:4字节,前一个页的指针,因为我们现在只有一个page,所以为FF FF FF FFFF FF FF FF:4字节,下一个页的指针,因为我们现在只有一个page,所以为FF FF FF FF00 00 00 11 4E 07 B2 CA:8字节,page的LSN45 BF:2字节,页的类型,表示这个page是数据页00 00 00 00 00 00 00 00:8字节,独立表空间该值都为000 00 04 D2:4字节,表示SPACE ID,表空间ID可以在informa shema下的INNODBSYSTABLESPACES表查到

再看Page Header 56字节

PAGENDIR_SLOTS(2字节):00 02表示只有2个slot,每个slot占用2个字节,结束偏移量为0ff7,那么开始偏移量就为0ff4,也就是00 70 00 63,倒序存储PAGEHEAPTOP(2字节):00 C1,0000c000 + c1 = c0c1,即最后一条记录往后一个位置PAGENHEAP(2字节):80 05,表示page内共有5条记录,?PAGE_FREE(2字节):00 00,表示可重用空间首地址PAGE_GARBBAGE(2字节):00 00:表示删除的记录字节数PAGELASTINSERT(2字节):00 AF:最后插入位置,C000+00AF= C0AFPAGEDIRECTION(2字节):00 02:表示PAGENO_DIRECTIONPAGENDIRECTION(2字节):00 02,一个方向连续插入记录的数量PAGENRECS(2字节):00 03,页中包含的记录数,注意不包含最大最小记录PAGEMAXTRX_ID(8字节):00 00 00 00 00 00 00 00,修改当前页的最大事务ID,注意该值仅在Secondary Index中定义PAGE_LEVEL(2字节):00 00,B树的高度PAGEINDEXID(8字节):00 00 00 00 00 00 08 48,索引ID,该值可以在informationschema下的INNODBSYS_INDEXES查到PAGEBTRSEG_LEAF(10字节):00 00 04 D2 00 00 00 02 00 F2:B+树数据页非叶节点所在段的segment header。注意该值仅在B+数的Root页中定义PAGEBTRSEG_TOP(10字节):00 00 04 D2 00 00 00 02 00 32:B+树数据页所在段的segment header。注意该值仅在B+数的Root页中定义

最大最小虚拟记录:

infimum:01 00 02 00 1C 69 6E 66 69 6D 75 6D 00

Record header:01 00 02 00 1C(其中1c表示下一条记录的偏移量)行记录:69 6E 66 69 6D 75 6D 00 可以计算出来的第一条用户记录的偏移量就是c063+1c=0c7f

supremum:02 00 0B 00 00 73 75 70 72 65 6D 75 6D

Record header:02 00 0B 00 00行记录:73 75 70 72 65 6D 75 6D

用户记录:

为了表示得更加清楚,我把两条虚拟记录位置也标注出来

<code>c063->99infimum:01 00 02 00 1C |69 6E 66 69 6D 75 6D 00/<code>

<code>c070->112supremum:02 00 0B 00 00 73 75 70 72 65 6D 75 6D/<code>

record1:

<code>c07f>12701 00 00 00 10 00 18 |80 00 00 01 |00 00 00 0C 81 E7 |B5 00 00 00 05 01 10 |61/<code>record header:01 00 00 00 10 00 1801表示变长字段,因为我们这里c1字段是变长字段,且占用空间小于255字节,占用1字节00表示null bit map,因为我们这里c1字段不为空,因此为0000 00 10 ->00000000 00000000 00010000,0000info flag,0000 record owned, 00000000 00010表示heap no=200 18表示下条记录的相对偏移量80 00 00 01:主键ID,4字节,没有指明无符号,因此是80 xxx开头00 00 00 0C 81 E7:trx_id,6字节B5 00 00 00 05 01 10:回滚段指针,7字节61:c1列,61表示字符a

record2:

<code>c097->15101 00 00 18 00 18 |80 00 00 02 |00 00 00 0C 81 E8 |B6 00 00 00 05 01 10 |/<code>record header:01 00 00 18 00 1800 00 18 -> 00000000 00000000 00011000,0000info flag,0000 record owned, 00000000 00011表示heap no=300 18表示下条记录的相对偏移量分析同上

record3:

<code>c08f->17501 00 00 00 20 FF C1 |80 00 00 03 |00 00 00 0C 81 E9 |B7 00 00 00 05 01 10 |61/<code>record header:01 00 00 00 20 FF C100 00 20 -> 00000000 00000000 00100000,0000info flag,0000 record owned, 00000000 00100表示heap no=4分析同上

Page Directory:

00 70 00 63 page directory是倒序存储的,因此00 63是最初行的相对位置,即c063,也就是infimum记录的起始位置,00 70对应位置0c70,对应supremum的起始位置

page tailer

38 AD C1 F5 : checksum值4E 07 B2 CA:对应LSN的低4字节

额外实验

<code>root@mysqldb 15:34: [xucl]> show create table t\G*************************** 1. row *************************** Table: tCreate Table: CREATE TABLE `t` ( `id` int(11) NOT NULL AUTO_INCREMENT, `c1` varchar(10) DEFAULT NULL, `c2` varchar(10) DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb41 row in set (0.00 sec)root@mysqldb 15:34: [xucl]> select * from t;+----+------+------+| id | c1 | c2 |+----+------+------+| 1 | a | NULL || 2 | a | a || 3 | NULL | NULL |+----+------+------+3 rows in set (0.00 sec)/<code>

提取用户记录部分

<code>01 02 |00 00 10 00 1A |80 00 00 01 00 00 00 0C 81 ED BA 00 00 00 05 01 10 |61 01 01 00 |00 00 18 00 19 |80 00 00 02 00 00 00 0C 81 F5 C0 00 00 00 05 01 10 |61 6103 |00 00 20 FF BE |80 00 00 03 00 00 00 0C 81 FD A6 00 00 00 05 01 10 |/<code>

为了观察Null和变长字段,我额外做了一个实验

01 02 -> 00000001 00000010,第一个字节表示变长字段c1长度为1,第二个字节表示第二个列c2为null01 01 00 -> 00000001 00000001 00000000,第一个字节表示变长字段c1长度为1,第二个字节表示变长字段c2长度为1,第三个字节表示没有字段为null03 -> 00000011,改字节表示第二个列、第三个列均为null

上次遗留问题,关于heap_top如何计算出来?

heaptop的其实就是最后一条记录往后第一个free的位置,加入最后一条记录的偏移量为189,假设最后一行的长度为32字节(5字节record header + 27字节),那么heaptop就是189+27=216了。


总结

简直手把手教了如何通过ue或者hexdump来观察innodb page结构肉眼查看确实比较费力,建议还是使用专业工具通过这种方法,能够对innodb page有更加深入的理解,例如上次遗留的问题就通过这次实验解惑了