目录
3. Infimum和Supremum Records(最小/最大虚拟行记录)
InnoDB存储引擎中,表记录都是根据主键顺序组织存放,这种存储方式的表称为索引组织表(index organized table)。每张表都有主键(隐藏列rowId),如果没有显式定义主键,则InnoDB存储引擎会自动创建6字节大小主键。
InnoDB存储引擎的所有数据都被逻辑地存放一个空间中,称为表空间(tablespace)。表空间由段(segment)、区(extent)、页(page)、行(row)组成,如下图所示InnoDB的逻辑存储结构。
表空间是InnoDB逻辑结构的最高层,所有的数据都放在表空间中。默认情况下,InnoDB存储引擎都有一个共享表空间ibdata1(数据目录下),即所有表的数据都存放在ibdata1空间内。通过innodb_data_file_path参数,可以定义多文件为一个表空间,默认数据目录下ibdata1文件。
如果启用了innodb_file_per_table参数(默认开启ON),每张表都有自己的独立表空间。独立表空间存放的只是数据、索引和插入缓冲Bitmap页,其它类的数据,如回滚(undo)信息,插入缓冲索引页、系统事务信息、二次写缓冲等还是存放在原来的共享表空间。
表空间由各个段组成,常见的段:数据段、索引段、回滚段。
InnoDB存储引擎表是索引组织的,因此数据即索引,索引即数据。对段的管理是由引擎自身完成。
区是由连续页组成的空间,在任何情况下每个区的大小都为1MB。为了保证区中页的连续性,InonoDB存储引擎一次从磁盘申请4-5个区。在默认情况下,InnoDB存储引擎的页的大小为16KB,即一个区中应有64个连续的页。
参数innodb_page_size控制页的大小,默认16KB。可以设置4K、8K,此时数据页不是压缩页,这是区中的页数分别为256、128。注意无论页的大小怎么变化,区大小总是1MB。
启用了innodb_file_per_table参数,每个表有自己的表空间。当创建表没有任何数据时,表空间大小96KB(6个连续页)。为什么不是1MB(一个区的大小)呢?原因是每个段开始时,先用32个碎片页(fragment page)来存放数据,用完后,新页则采用区的方式申请。这样做的目的:对于一些小表,开始时申请较少的空间,来节省磁盘容量的开销。如下代码,是初始化大小。
- mysql> SHOW CREATE TABLE test_init_table\G;
- *************************** 1. row ***************************
- Table: test_init_table
- Create Table: CREATE TABLE `test_init_table` (
- `id` int(11) NOT NULL,
- `name` varchar(255) DEFAULT NULL,
- PRIMARY KEY (`id`)
- ) ENGINE=InnoDB DEFAULT CHARSET=utf8
- 1 row in set (0.00 sec)
-
- ERROR:
- No query specified
-
- mysql>
- mysql>
- mysql> system ls -lh /home/MySQL5.7/mysql-5.7.35/data/test_mysql/test_init_table.ibd;
- -rw-r----- 1 mysql mysql 96K Jul 26 02:03 /home/MySQL5.7/mysql-5.7.35/data/test_mysql/test_init_table.ibd
使用py_innodb_page_info.py脚本工具,查询页的信息:总页数为6,6 * 16KB = 96KB。
- [root@488c1daa7967 py_innodb_page_info]# python py_innodb_page_info.py -v /home/MySQL5.7/mysql-5.7.35/data/test_mysql/test_init_table.ibd
- page offset 00000000, page type
- page offset 00000001, page type
- page offset 00000002, page type
- page offset 00000003, page type
, page level <0000> - page offset 00000000, page type
- page offset 00000000, page type
- Total number of page: 6:
- Freshly Allocated Page: 2
- Insert Buffer Bitmap: 1
- File Space Header: 1
- B-tree Node: 1
- File Segment inode: 1
页,也称块(block),是InnoDB磁盘管理的最小单元。静态参数innodb_page_size控制页的大小,默认16KB。可以设置4K、8K。InnoDB存储引擎中,常见的页类型有:
InnoDB存储引擎是面向列的(row-oriented),则按行进行存放。每个页存放的行记录也是有硬性定义的,最多允许存放16KB/2-200。
每页最少存储2行记录,用链表连接起来,否则会失去B+树的意义。数据大的行记录,如:大字符串、TEXT、BLOB对象,都是采用行溢出数据存储。不同的行格式,存储方式不同,详细情况见下章节。
行记录格式名称都是取自动物的名字,并按照字母顺序排序,且后一个格式支持前一个格式。参数innodb_file_format查看当前支持的行格式。MySQL5.7版本的行格式是Barracuda,同时支持之前的格式Antelope。不同格式的特性,详细参考地址:MySQL :: MySQL 5.7 Reference Manual :: 14.11 InnoDB Row Formats
- mysql> show variables like 'innodb_file_format';
- +--------------------+-----------+
- | Variable_name | Value |
- +--------------------+-----------+
- | innodb_file_format | Barracuda |
- +--------------------+-----------+
- 1 row in set (0.02 sec)
Compact是MySQL5.0引入,目的是高效的存储数据:页中行数据越多,则性能越高。如下图所示,是Compact存储方式。
行格式的首部是个非NULL的变长字段长度列表,且按列的顺序逆序放置,长度为:
NULL标记位占2Byte,例如:NULL标记位的值06,转为二进制00000110,为1值的位表示第2列和第3列的值为NULL。注意,列值为NULL时,不占用任何位置。无论CHAR或VARCHAR类型,compact格式下NULL值不占任何存储空间。
下表是Compact格式的记录头信息(5Byte)。记录是以堆的形式存放;n_owned是页目录slots中的记录数;每个记录通过next_record找到下一个记录的相对位置,即:页中的行链表的形式链接。
列数据,是按列的顺序放置,注意:NULL值的列不占用任何存储空间。有3个隐藏列:事务ID(6Byte) 、回滚指针(7Byte)、rowid(6Byte)。
大VARCHAR、TEXT、BOLB类型,一部分存储在数据页(B-tree node),另一部分数据存储在数据页之外(行溢出数据),指针指向页类型为未压缩二进制大对象页(Uncompress BLOB)。
Redundant是MySQL5.0之前的格式,如下图所示。
与Compact不同之处:
Dynamic格式是Compact的升级版。大VARCHAR、TEXT、BOLB类型,采用完全的行溢出方式,20字节的指针指向Off Page。
Compressed格式是Compact的升级版。大VARCHAR、TEXT、BOLB类型,采用完全的行溢出方式,20字节的指针指向Off Page。另外一功能,行数据以zlib的算法进行压缩,即:压缩页。
文件格式 | 行格式 | 特点 |
Antelope (羚羊) | Compact (紧凑) | 1. 目的:高效存储数据,即:页中行越多,则性能越大; 2. 行格式:变长字段长度偏移列表(1或2字节) + NULL标记位(1字节) + 记录头信息(5字节) + 列数据 ... + 隐藏事务ID列(6字节) + 隐藏回滚指针列(7字节) + rowid(6字节); 3. 行记录首部是非NULL变长字段,按列顺序逆序放置: 列长度小于255字节,用1字节表示; 大于255字节,用2字节表示; 4. CHAR或VARCHAR类型,NULL值的列不占任何空间; 5. 行中有NULL值的列,NULL标记位(转二进制)中该列的位置位则为1,否则0; 6. 固定长度CHAR未能完全占用,则用0x20填充; 7. 大VARCHAR、TEXT、BOLB类型,部分数据存储在数据页之外(行溢出数据),指针指向另存的未压缩二进制大对象页(Uncompress BLOB)。 |
Redundant (冗余) | 1. 行格式:字段长度偏移列表(1或2字节) + 记录头信息(6字节) + 列数据 ... + 隐藏事务ID列(6字节) + 隐藏回滚指针列(7字节) + rowid(6字节); 2. 没有NULL标志位,与Compact不同:CHAR的NULL值占用空间;VARCHAR的NULL值不占用空间; 3. 大VARCHAR、TEXT、BOLB类型,部分数据存储在数据页之外(行溢出数据),指针指向另存的未压缩二进制大对象页(Uncompress BLOB)。 | |
Barracuda (梭子鱼) | 包括Antelope | |
Dynamic (动态) | 1. 行格式:Compact升级版; 2. 大VARCHAR、TEXT、BOLB类型,采用完全的行溢出方式,20字节的指针指向Off Page。 | |
Compressed (压缩) | 1. 行格式:Compact升级版; 2. 行记录以zlib的算法进行压缩; 3. 大VARCHAR、TEXT、BOLB类型,采用完全的行溢出方式,20字节的指针指向Off Page。 |
页,也称块(block),是InnoDB磁盘管理的最小单元。页类型为B-tree node的页存放的即是表中行的数据。下图所示,是InnoDB存储引擎数据页结构。其中File Header、Page Header、File Trailer的大小固定,分别是38Byte、56Byte、8Byte,其他的大小都是动态。
InnoDB数据页结构,有7部分组成,如下表所示。
组成 | 描述 |
File Header(文件头) | 页的一些头信息,如:该页的偏移量、上下页相对位置、LSN等 |
Page Header(页头) | 页的状态信息,如:页在索引中的位置、最大事务ID、slots数等 |
Infimum和Supremum Records (最小/最大虚拟行记录) | 1. 虚拟的行,用来限定User Records的边界; 2. Infimum记录比该页中任何主键值都要小的值; Supremum记录比该页中任何主键值都要大的值; 3. 虚拟记录,在页被创建时被建立,任何情况下无法被删除。 |
User Records(用户记录) | 实际存储的行记录数据 |
Free Space(空闲空间) | 1. 空闲空间,用链表数据结构; 2. 当一个记录被删除后,则该空间会被加入到空闲链表。 |
Page Directory(页目录) | 1. 存放记录的相对位置(页中的相对位置,不是偏移量); 2. 有些记录指针称为槽(Slots)或目录槽(Directory Slots); 3. 稀疏目录(sparse directory):一个槽中可能包含多个记录; 4. Slots中的记录按索引键值顺序存放,二叉查找法找到记录指针。 |
File Trailer(文件结尾信息) | 1. 检测页是否完整的写入磁盘(如:磁盘损坏、宕机等); 2. 默认情况下,InnoDB引擎每次从磁盘读取页时,就会检测该页的完整性,由innodb_checksums参数,默认开启ON; 3. innodb_checksum_algorithm控制测试checksum函数的算法,默认crc32。 |
File Header(文件头)用来记录页的头信息,共占用38Byte。下表所示:File Header组成部分、InnoDB引擎页的类型。
Page Header(页头)用来记录数据页的状态信息,共占用56Byte。下表所示:Page Header组成部分。
InnoDB引擎中,每个数据页都有虚拟的行记录,用来限定 User Records(行记录)的边界。Infimum记录比该页中任何主键值都要小的值;Supremum记录比该页中任何主键值都要大的值。注意:这两个虚拟记录,在页被创建时被建立,任何情况下无法被删除。
User Records(用户记录),也称行记录,实际存储行记录的内容。 InnoDB存储引擎表是索引组织的,总是B+树组织存放数据。
Free Space(空闲空间),指空闲的空间,也是链表数据结构。当一个记录被删除后,则该空间会被加入到空闲链表。
Page Directory(页目录) 存放记录的相对位置(页中的相对位置,不是偏移量)。InnoDB中有些记录指针称为槽(Slots)或目录槽(Directory Slots),并不是每个拥有一个槽。页目录是个稀疏目录(sparse directory),即:一个槽中可能包含多个记录。
Slots中的记录按索引键值顺序存放,目的使用二叉查找法找到记录指针。页目录很好的解释了File Header中的n_owned值的含义,因为有些记录不在页目录中。所以记录查找的过程是如下所示:
File Trailer(文件结尾信息)用来检测页是否完整的写入磁盘(如:磁盘损坏、宕机等)。默认情况下,InnoDB引擎每次从磁盘读取页时,就会检测该页的完整性。参数innodb_checksums控制是否开启页的完整性检查,默认开启ON。
File Trailer占用8Byte,只有一个FIL_PAGE_END_LSN。前4字节是该页的checksum值,后4字节和File Header的FIL_PAGE_LSN相同。参数innodb_checksum_algorithm控制测试checksum函数的算法,默认crc32。checksum通过指定函数进行比较,是否一致,来完成页的完整性检查。
MySQL高级进阶:关于InnoDB存储结构,一文深入分析讲解-51CTO.COM
MySQL表(二)-InnoDB 行 记录格式_星光之子0317的博客-CSDN博客_innodb 行记录格式
MySQL :: MySQL 5.7 Reference Manual :: 14.11 InnoDB Row Formats