页是InnoDB中磁盘和内存交互的基本单位,也是InnoDB管理存储空间的基本单位,默认大小是16KB。 InnoDB数据存在磁盘,而数据的处理发生在内存,所以需要将磁盘的数据加载到内存中,由于磁盘相比于内存慢的多,于是InnoDB将数据划分为若干个页,以页作为磁盘和内存交互的基本单位。 系统变量innodb_page_size 指定了InnoDB存储引擎的页大小,默认为16384 即16KB,该变量只能在第一次初始化Mysql数据目录时指定
操作mysql时都是以记录为单位向表中插入数据的,这些记录在磁盘的存放形式被称为行格式或者记录格式。
InnoDB定义了四种行格式:
- COMPACT
- REDUNDANT
- DYNAMIC
- COMPRESSED
1、COMPACT 行格式
1.1、额外信息
(1)变长字段长度列表
mysql支持varchar,text等一些变长数据类型,拥有变长数据类型的变长字段中存储大小是不固定的,所以在存储真实数据时需要顺便将数据占用的字节数也存起来,存放在变长字段长度列表中。
变长字段长度列表各变长字段数据占用的字节数按照列的顺序逆序存放。
至于记录长度时用 1 个字节还是用 2 个字节,InnoDB 有一套规则。
- W:该字符集一个字符需要的字节数,如utf8mb4为4,utf为3,gbk为2,ascii为1
- M:该类型最多能存多少个字符,如(varchar(M),M表示最多能存储多少个字符)
- L:实际存储占用的字符数
计算数据占用字节数规则:
如果MW < 255,使用1字节表示真实数据占用字节数
如果MW < 255,则分为两种情况
如果L<= 127 使用1字节表示真实数据占用字节数
如果L>127 使用2字节表示真实数据占用字节数
(2) NULL 值列表
一条记录某些列可能存储null值,如果把这些null都放在记录的真实数据中会浪费存储空间,所以COMPACT格式将一条记录中值为null的列统一管理,存储到null值列表中。
用一位标记一个字段是否为 null,是 1 代表 null,0 代表 非null,逆序存放。整体以字节为最小单位,不足的高位补0。
如下所示,假如列1-列5都可以为null,那么需要一个字节存放null值列表,假如列1,列4,列5为null,如下图所示,此时NULL 值列表值为
0x13(16进制)
(3)记录头信息
| 名称 | 大小(位) | 描述 |
|---|---|---|
| 预留位1 | 1 | 没有使用 |
| 预留位2 | 1 | 没有使用 |
| delete_flag | 1 | 标识当前记录是否被删除 |
| min_rec_flag | 1 | B+树的每层非叶子节点中的最小记录都会添加该标记 |
| n_owned | 4 | 数据页会将记录分组,组内地址偏移量最大的记录会统计该组的记录数 |
| heap_no | 13 | 表示当前记录在页面堆中的相对位置。 |
| record_type | 3 | 当前记录的类型,0表示普通记录,1表示B+树非叶子节点记录,2表示最小记录(Inflimum),3表示最大记录(Superemum)。 |
| next_record | 16 | 下一条记录的相对位置 |
1.2、记录的真实数据
除了真实的列数据以外,mysql还会为每条记录添加隐藏列。
| 列名 | 是否必须 | 占用空间 | 描述 |
|---|---|---|---|
| row_id | 否 | 6字节 | 优先使用用户自定义主键,如果没有自定义主键,则选择一个不允许存储null值的唯一索引列作为键,如果表中没有不允许存储null值的唯一索引列,则InnoDB会为表默认添加一个名为row_id的隐藏列做主键。 |
| trx_id | 是 | 6字节 | 事务id |
| roll_pointer | 是 | 7字节 | 回滚指针 |
- row_id:行唯一标识ID(非必须)
- trx_id:事物ID
-
2、Redundant 行格式
Redundant行格式是MySQL 5.0之前用的一种行格式,已经弃用,不用太关注。
2.1、额外信息
(1) 字段长度偏移列表
Compact格式记录的是长度可变的字段的长度,而Redundant记录的是所有字段的偏移量,一样是逆序存放。相邻两个字段偏移量的差就是该字段的长度。
(2) 记录头信息
两个预留位,没有使用。
- delete_flag:删除标记
- min_rec_flag:B+树的每层非叶子节点中的最小记录都会添加该标记
- n_owned:当前记录拥有的记录数
- heap_no:当前记录在页面堆的位置信息
- n_field:当前记录中列的数量
- 1byte_offs_flag:标记字段长度偏移列表中的偏移量是使用1字节还是2字节表示的
- next_record:表示下一条记录的相对位置
和Compact相比多了n_field和1byte_offs_flag,少了record_type。
由于记录中没有 NULL 值表,记录 NULL 值的策略如下:
如果 NULL 的字段是长度不可变类型,直接用 0x00 填充。
如果 NULL 的字段是可变长度类型,那么在字段长度偏移列表中这个字段不占长度,即这个字段的偏移量和下一个字段的偏移量相同,真实数据区域不会存任何东西。
除了以上几点,和Compact的格式大致还是相同的。
3、Dynamic 格式
MySQL 5.7默认的行格式就是Dynamic,它和Compact仅仅是在行溢出时有区别,Dynamic行格式不会记录列的前768个字节,而是把所有数据都存放到溢出页中。溢出列相关说明见下面溢出列处理。
4、Compressed 格式
逻辑和Dynamic一致,但增加了压缩算法,对页进行压缩,节省空间。
溢出列处理
因为InnoDB页大小是16KB,如果数据记录的某个列占用大小很大,如varchar(5000) utf8mb4格式,该列就需要5000*4= 20000字节,而一页才16KB即16384字节,显然一页都放不下一列,于是IndoDB采取了溢出页来处理这种情况。
对于COMPACT和REDUNDANT行格式数据来说,如果一列中数据很多,则在本记录的真实数据处只会存储该列的前768字节以及指向其它页面的地址,把剩下的数据存放在其它页,这些存储768字节之外的数据页面称为溢出页。
Dynamic行格式不会记录列的前768个字节,而是把所有数据都存放到溢出页中。
