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