本文介绍了MySQL InnoDB空间文件的概念及其基本结构。

简介

InnoDB除重做日志外均使用统一的结构进行管理,包括系统表空间、用户表空间、撤销日志以及临时表空间。这个结构统称空间文件。

inno_space

Inno_space是一个可以直接访问InnoDB内部文件的命令行工具,可以打印出文件的内部结构。它不依赖任何外部文件,只需要执行make命令就可以得到可执行文件,做到了开箱即用。

Inno_space除支持打印出文件的具体结构之外,同时还能修复损坏页面。例如,InnoDB表文件中的页面损坏导致实例无法正常启动。如果损坏的只是叶子页,inno_space可以跳过损坏的页面,从而保证实例能够启动,并且将找回大部分的数据。

Inno_space还提供分析表文件中的数据的功能。例如,是否存在过多的空闲页面,从而给用户建议是否需要执行优化表(Optimize Table)操作等。

InnoDB空间文件基本结构

InnoDB空间文件管理整个InnoDB文件系统。基本结构如下:
  • 在InnoDB中,16 KB大小的页(page)是最小的原子单元。其他的大小都在页之上,因此:

    1 page=16 KB=16384 bytes

    1 extent=64 pages=1 MB

    FSP_HDR page=256 extents=16384 pages=256 MB

    Page有最基础的38字节的FIL Header和8字节的FIL Trailer。

    InnoDB_space_file
    主要内容有:
    1. Checksum:当前页面的checksum,用来判断页面是否有损坏。
    2. Page Number:Page Number可以计算出页面在文件上的偏移量。也可以参考Page Number值来判断一个页面是否被初始化。
    3. Previous Page/Next Page:该字段只有在索引页面才有意义,而且只有在叶子页时才有用,在非叶子页时没有意义。
    4. LSN for last page modification:最后一次修改页面的64位日志序列号LSN。
    5. Page Type:当前页的具体类型。例如:btree index leaf-page、undo log page、btree index non-leaf page、insert buffer、fresh allocated page、属于ibdata1的系统页等。页类型决定当前页的用途。
    6. Flush LSN:保存的是已经刷到磁盘的页面的最大LSN信息。只有在space 0 page 0这个页面中有意义。
      • 用途:在实例启动时读取Flush LSN,可以确保在Flush LSN之前的页面已经刷入磁盘。Flush LSN之后的重做日志是没有检查点的日志,但其实重做日志里面已经存在检查点信息。
      • 写入:在进行宕机或强制执行检查点(checkpoint)时,通过fil_write_flushed_lsn_to_data_files写入。
      • 读取:在实例启动时,fil_read_first_page会读取LSN信息, 用于更新启动时的min_flushed_lsnmax_flushed_lsn。因为重做日志模块还没有初始化,可以参考这两个LSN做一些简单的判断。
    7. Space ID:存储当前页所在的空间ID(MySQL 8.0已经将该字段删除)。
    通过inno_space命令行工具可以查看文件对应的内部结构:
    ./inno -f ~/git/primary/dbs2250/sbtest/sbtest1.ibd -p 10
    查询结果如下:
    ==========================block==========================
    FIL Header:
    CheckSum: 2065869235
    Page number: 10
    Previous Page: 9
    Next Page: 11
    Page LSN: 554513658770
    Page Type: 17855
    Flush LSN: 0 
  • 空间文件(Space file)
    一个空间文件是232个页面的合集,连续64个页面称之为extent(区),256个连续的区会有一个XDES(extent descriptor)进行管理,第一个XDES称之为FSP_HDR,除此之外还有一些额外的信息。下图为基本文件组织结构,无论是撤销空间、系统空间还是用户的表空间都是这种结构:Space file上图可以看出:
    • page 0:FSP_HDR(file space header)。FSP_HDR页包含一个FSP头结构,记录空间大小和空闲页列表等信息。
    • page 1:插入缓冲位图页(insert buffer bitmap)。
    • page 2:INODE页,用于存储有文件段相关的列表。
  • 系统空间(The system space)
    系统空间的space id=0,文件名为ibdata1,即系统文件。The system space
    • page 0,1,2 :分别为FSP_HDR页、IBUF_BITMAP页和INODE页。
    • page 3:保存和插入缓存相关的信息。
    • page 4:保存插入缓存的索引结构的root页,插入缓存也是一个Btree树。
    • page 5:记录和InnoDB事务操作相关的信息,例如最近一次的事务ID、MySQL的二进制日志信息以及双写缓冲区的位置信息。
    • page 6:保存第一个回滚段(rollback segment)页,您可以根据需要分配额外的页或者整个区段来存储回滚段数据。 page 6
    • page 7:保存与数据字典相关的头信息。
    • page 64-127:保存双写缓冲区的第一个块。
    • page 128-191:保存双写缓冲区的第二个块。
    系统表空间其他页所在的索引、回滚段和重做日志等,当您需要时再进行分配。
    通过inno_space打开ibdata1文件可以查看如下信息:
    File path /home/zongzhi.czz/git/primary/log2250/ibdata1 path
    File size 209715200
    start           end             count           type
    0               0               1               FSP HDR
    1               1               1               INSERT BUFFER BITMAP
    2               2               1               INDEX NODE PAGE
    3               3               1               SYSTEM PAGE
    4               4               1               INDEX PAGE
    5               5               1               TRX SYSTEM PAGE
    6               7               2               SYSTEM PAGE
    8               8               1               SDI INDEX PAGE
    9               12799           12790           FRESHLY ALLOCATED PAGE
    通过inno_space打开一个普通的用户表空间:
    /inno -f ~/git/primary/dbs2250/sbtest/sbtest1.ibd -c list-page-type
    查询结果如下:
    File path /home/zongzhi.czz/git/primary/dbs2250/sbtest/sbtest1.ibd path, page num 0
    page num 0
    ==========================space page type==========================
    File size 2604662784
    start           end             count           type
    0               0               1               FSP HDR
    1               1               1               INSERT BUFFER BITMAP
    2               2               1               INDEX NODE PAGE
    3               3               1               SDI INDEX PAGE
    4               16383           16380           INDEX PAGE
    16384           16384           1               XDES
    16385           16385           1               INSERT BUFFER BITMAP
    16386           31990           15605           INDEX PAGE
    31991           31999           9               FRESHLY ALLOCATED PAGE
    32000           32767           768             INDEX PAGE
    32768           32768           1               XDES
    32769           32769           1               INSERT BUFFER BITMAP
    32770           49151           16382           INDEX PAGE
    49152           49152           1               XDES
    49153           49153           1               INSERT BUFFER BITMAP
    49154           65535           16382           INDEX PAGE
    65536           65536           1               XDES
    65537           65537           1               INSERT BUFFER BITMAP
    65538           81919           16382           INDEX PAGE
    81920           81920           1               XDES
  • 单独表空间(File Per Table)
    .ibd为后缀的文件即单独表空间文件。结构如下:File Per Table
    • page 0,1,2:分别为FSP_HDR页、IBUF_BITMAP页和INODE页。
    • page 3:主键聚簇的根(root)页。
    • page 4:二级聚簇的根页。由CREATE TABLE指定。例如:如下page 4是k_1索引的root页。
      Create Table: CREATE TABLE `sbtest1` (
        `id` int(11) NOT NULL AUTO_INCREMENT,
        `k` int(11) NOT NULL DEFAULT '0',
        `c` char(120) NOT NULL DEFAULT '',
        `pad` char(60) NOT NULL DEFAULT '',
        PRIMARY KEY (`id`),
        KEY `k_1` (`k`)
      ) ENGINE=InnoDB AUTO_INCREMENT=237723 DEFAULT CHARSET=latin1
      1 row in set (0.00 sec)
      如果运行过程中再添加新的索引页,新的索引的根页会分散在其他页上。
      例如:执行ALTER TABLE,额外增加一个索引:
      alter table sbtest1 add index idx_c(c);
      通过inno_space工具可以看到每一个索引的根页信息。
      ./inno -f ~/git/primary/dbs2250/sbtest/sbtest1.ibd -c index-summary
      查询结果如下:
      File path /home/zongzhi.czz/git/primary/dbs2250/sbtest/sbtest1.ibd path, page num 0
      ==========================Space Header==========================
      Space ID: 15
      Highest Page number: 158976
      Free limit Page Number: 152256
      FREE_FRAG page number: 24
      Next Seg ID: 7
      File size 2604662784
      ========Primary index========
      Primary index root page space_id 15 page_no 4
      Btree hight: 2
      <<<Leaf page segment>>>
      SEGMENT id 4, space id 15
      Extents information:
      FULL extent list size 2140
      FREE extent list size 0
      PARTIALLY FREE extent list size 1
      Pages information:
      Reserved page num: 137056
      Used page num: 137003
      Free page num: 53
      
      <<<Non-Leaf page segment>>>
      SEGMENT id 3, space id 15
      Extents information:
      FULL extent list size 1
      FREE extent list size 0
      PARTIALLY FREE extent list size 1
      Pages information:
      Reserved page num: 160
      Used page num: 116
      Free page num: 44
      
      ========Secondary index========
      Secondary index root page space_id 15 page_no 31940
      Btree hight: 2
      <<<Leaf page segment>>>
      SEGMENT id 6, space id 15
      Extents information:
      FULL extent list size 7
      FREE extent list size 0
      PARTIALLY FREE extent list size 219
      Pages information:
      Reserved page num: 14465
      Used page num: 12160
      Free page num: 2305
      
      <<<Non-Leaf page segment>>>
      SEGMENT id 5, space id 15
      Extents information:
      FULL extent list size 0
      FREE extent list size 0
      PARTIALLY FREE extent list size 0
      Pages information:
      Reserved page num: 19
      Used page num: 19
      Free page num: 0
      
      **Suggestion**
      File size 2604662784, reserved but not used space 39354368, percentage 1.51%
      Optimize table will get new fie size 2565308416
      以上示例可以看出:
      • tablespace id为15。
      • Btree的高度为3层。
      • 由于Secondary Index只存索引,所以Primary Index占用的空间是Secondary Index的10倍。
      • Primary Index上大量的页都已经用尽, 而Secondary Index会有20%左右的空闲页。
      整体而言,空闲页只占了文件的1.51%左右,所以不需要进行表优化(Optimize Table)。