为什么别人能用好 MySQL?万字详解其复杂原理( 五 )

  • INDEX Header(36字节): 记录的是 INDEX 页的状态信息 。前2字节 00 02 表示页目录的 slot 数目为2;接着2字节 00 b0 是页中第一个记录的指针 。80 04是这页的格式为DYNAMIC和记录数4(包括2条System Records我们插入的2条记录) 。接着 00 00是可重用空间首指针,再后面2字节00 00是已删除记录数;00 9a是最后插入记录的位置偏移,即最后插入位置是 0xc09a,即第2条记录开始地址 。00 02 是最后插入的方向,2 表示 PAGE_DIRECTION_RIGHT,即自增长方式插入 。00 01 指一个方向连续插入的数量,这里为1 。接着的00 02是 INDEX 页中的真实记录数,我们只有2条记录 。然后8字节00…00为修改该页的最大事务ID,这个值只在辅助索引中存在,这里为0 。接着2字节00 00为页在索引树的层级,0表示叶子结点 。最后8个字节 00…2f为索引ID 47(索引ID可以在information_schema.INNODB_SYS_INDEXES 中查询,可以确认 47 正好是表 t 的主索引) 。
  • FSEG Header:这是INDEX页中的根结点才有的,非根结点的为0 。前10字节 00 00 00 1c 00 00 00 02 00 f2 是叶子结点所在段的segment header,分别记录了叶子结点的表空间ID 0x1c,INODE页的页号 2 和 INODE项偏移 0xf2 。而后10字节 00 00 00 1c 00 00 00 02 00 32 是非叶子结点所在段的segment header,偏移分别是0xf2 和 0x32,即INODE页的前2个Entry,文件段ID分别是1和2 。FSEG Header中存储了该 INDEX 页的INODE项,INODE项里面则记录了该页存储所在的文件段以及文件段页的使用情况 。对于 File-Per-Table情况下,每个单独的表空间文件的 FSP_HDR 页负责管理页使用情况 。

  • 为什么别人能用好 MySQL?万字详解其复杂原理

    文章插图
     
    FSEG结构关系图
    • System Records(26字节): 每个 INDEX 页都有两条虚拟记录 infimum 和 supremum,用于限定记录的边界,各占 13 个字节 。其中记录头的5个字节分别标识了拥有记录的数目和类型(拥有记录数目是即后面页目录部分的owned值,当前页目录只有两个槽,infimum拥有记录数只有它自己为1,而supremum拥有我们插入的2条记录和它自己,故为3)、下一条记录的偏移 0x1c,即位置是 0xc07f,这就是我们实际记录开始位置 。后面8个字节为 infimum + 空值,supremum类似,只是它下一条记录偏移为0 。01 00 02 00 1c 69 6e 66 69 6d 75 6d 00 # infimum
      03 00 0b 00 00 73 75 70 72 65 6d 75 6d # supermum
    • User Records: 接下来是2条我们插入的记录 。第1条记录前面7字节是记录头(Record Header),其中前面的 1字节记录的是可变变量的长度03,因为我们记录中c的值是 abc 。然后1字节记录的是可为NULL的变量是否是NULL,这里不为 NULL,故为0 。接着的5字节记录了插入顺序2(infimum插入顺序固定是0,supremum插入顺序是1,其他记录则是从2开始),下一个记录的偏移 0x1b(即下一个记录开始位置是0xc078+0x1b=0xc093),删除标记等 。后面就是记录内容 。第2条记录同理 。这里的事务ID可以通过 select * from information_schema.innodb_trx 进行验证 。
     03 00 00 00 10 00 1b # 记录头 80 00 00 01          # 主键值1 00 00 00 00 05 68    # 事务ID d1 00 00 01 54 01 10 # 回滚指针 61 62 63             # ch的值 abc 05 00 00 00 18 ff d6 # 第2条记录头 80 00 00 02          # 主键值2 00 00 00 00 05 69    # 事务ID d2 00 00 01 55 01 10 # 回滚指针 64 65 66 67 68       # ch的值 defghB+树页详细结构
    • Page Directory(4字节):因为页目录的slot只有2个,每个slot占2字节,故页目录为 00 70 00 63 这4字节,存储的是相对于最初行的位置 。其中 0xc063 正好是 infimum 记录的开始位置,而 0xc070 正好是 supremum 记录的开始位置 。使用页目录进行二分查找,可以加速查询,详细见后面分析 。
    • FIL Tail (8字节): 最后8字节为 95 45 82 8a 00 28 85 7c,其中 95 45 82 8a 为 checknum,跟 FIL Header的checksum一样 。后4字节00 28 85 7c 与 FIL Header的LSN的后4个字节一致 。
    当然,我们也可以通过 innodb_ruby 工具来分析表空间文件 。
    root@stretch:/home/vagrant# innodb_space -s /var/lib/mysql/ibdata1 -T test/t space-page-type-regionsstart       end         count       type                0           0           1           FSP_HDR             1           1           1           IBUF_BITMAP         2           2           1           INODE               3           3           1           INDEX               4           5           2           FREE (ALLOCATED)    root@stretch:/home/vagrant# innodb_space -s /var/lib/mysql/ibdata1 -T test/t -p 3 page-recordsRecord 127: (id=1) → (ch="abc")Record 154: (id=2) → (ch="defgh")


    推荐阅读