本文介绍了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。
主要内容有:- Checksum:当前页面的checksum,用来判断页面是否有损坏。
- Page Number:Page Number可以计算出页面在文件上的偏移量。也可以参考Page Number值来判断一个页面是否被初始化。
- Previous Page/Next Page:该字段只有在索引页面才有意义,而且只有在叶子页时才有用,在非叶子页时没有意义。
- LSN for last page modification:最后一次修改页面的64位日志序列号LSN。
- Page Type:当前页的具体类型。例如:btree index leaf-page、undo log page、btree index non-leaf page、insert buffer、fresh allocated page、属于ibdata1的系统页等。页类型决定当前页的用途。
- 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_lsn和max_flushed_lsn。因为重做日志模块还没有初始化,可以参考这两个LSN做一些简单的判断。
- 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,除此之外还有一些额外的信息。下图为基本文件组织结构,无论是撤销空间、系统空间还是用户的表空间都是这种结构:上图可以看出:
- 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,即系统文件。通过inno_space打开ibdata1文件可以查看如下信息:
- 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 7:保存与数据字典相关的头信息。
- page 64-127:保存双写缓冲区的第一个块。
- page 128-191:保存双写缓冲区的第二个块。
通过inno_space打开一个普通的用户表空间: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 -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
为后缀的文件即单独表空间文件。结构如下:- 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
,额外增加一个索引:
通过inno_space工具可以看到每一个索引的根页信息。alter table sbtest1 add index idx_c(c);
查询结果如下:./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%左右的空闲页。