MySQL InnoDB行格式
本文将介绍 MySQL 中 InnoDB 存储引擎的行格式。
一、概述
1. 什么是 InnoDB行格式?
在 MySQL 中,数据以记录的形式存储,这些记录在磁盘上的存放方式也被称为行格式。
在 InnoDB 中,有四种不同类型的行格式,分别为:
- Compact
- Redundant
- Dynamic
- Compressed
2. 指定行格式
可以在创建、修改表时指定行格式:
1 |
|
二、Compact 行格式
1. 记录的额外信息 - 变长字段长度列表
MySQL 支持一些变长类型,值的类型为变长类型的列被称为变长字段。
由于变长字段存储时所占用的字节数并不固定,因此需要在存储数据的同时额外存储其占用的字节数。
在 Compact 行格式中,所有变长字段的占用字节长度会组合为一个变长字段长度列表,存放在记录的开头位置。
需要注意的是:
变长字段长度列表中仅记录每个非 null 的变长字段的长度
2. 记录的额外信息 - NULL 值列表
Compact 行格式将所有 “允许 NULL 的列” 的 “NULL 与否信息” 统一存储为 NULL 值列表。
具体做法为:分配一块内存空间,其中,每个二进制位对应一个 “允许 NULL 的列”,用二进制值表示对应列的值是否为 NULL。
通过这样的方式,在占用极少空间的情况下,存储了各个 “允许 NULL 的列” 的 “NULL 与否信息”。
3. 记录的额外信息 - 记录头信息
记录头信息 描述该记录的存储信息,分别用二进制位表示了以下信息:
名称 | 大小 | 描述 |
---|---|---|
预留位1 | 1 | 没有使用 |
预留位2 | 1 | 没有使用 |
delete_mask | 1 | 标记该记录是否被删除 |
min_rec_mask | 1 | 表示该记录是否为 B+ 树中每层非叶子节点的最小记录 |
n_owned | 4 | 表示当前分组拥有的记录数 |
heap_no | 13 | 表示当前记录在记录堆的位置信息 |
record_type | 3 | 表示当前记录的类型,0 表示普通记录,1 表示 B+ 树非叶子节点记录(目录项记录),2 表示最小记录,3 表示最大记录 |
next_record | 16 | 表示下一条记录的相对位置 |
delete_mask:标记该记录是否被删除
之所以不直接删除记录,是因为移除他们以后将其它记录在磁盘上重新排列需要性能消耗,而更改删除标记带来的性能损耗要小得多。
所有被删除的记录都会组成 “垃圾链表”,链表中记录所占用的空间被称为 “可重用空间”,此后如果有新纪录插入,可能会覆盖这些存储空间。
heap_no:表示当前记录在记录堆的位置信息
值从 2 开始,这是因为 InnoDB 为每个页添加了两个 “伪记录”,它们分别代表 “最大记录”、”最小记录”,正是它们占用了 0 和 1 的编号。
next_record:表示下一条记录的相对位置
表示当前记录到下一条记录的地址偏移量。
这里的下一条记录,并不是按插入顺序排序,而是按主键值从小到大排序。
“最小记录” 的下一条记录为本页中主键值最小的记录;本页中主键值最大的记录的下一条记录是 “最大记录”。
InnoDB 会始终维护一个单链表,其中的各节点按照主键值从小到大顺序连接。
4. 记录的真实数据
记录的帧数数据中会存储所有非 null 的自定义列。
除了自定义的列外,MySQL 会为每个记录默认添加一些 “隐藏列”,如下:
- DB_ROW_ID:行 ID;唯一标识一条记录;可选,当表没有主键、没有 unique 键时才会添加
- DB_TRX_ID:事务 ID
- DB_ROLL_PTR:回滚指针
5. 行溢出
对于存储空间占用较大的列,会这样存储:
- 用一部分空间存储该列的一部分数据
- 剩余数据会被分散存储在其它页中,用另一部分空间存储这些数据的指针
三、Redundant行格式
1. 记录的额外信息 - 字段长度偏移列表
将所有列(包括隐藏列)的偏移信息存储到 字段长度偏移列表 中,计算列值长度需要计算差值
2. 记录的额外信息 - 记录头信息
同上。
3. 记录的真实数据
同上。
4. 行溢出
同上。
四、Dynamic 和 Compressed 行格式
Dynamic 和 Compressed 行格式与 Compact 行格式类似,区别在于:行溢出时,Dynamic 和 Compressed 行格式会将所有数据均存储到其它页面,行中只存储指针。
Dynamic 和 Compressed 行格式的不同点是:Compressed 行格式会采用压缩算法对页面进行压缩,以节省空间。
参考
- MySQL 技术内幕
- MySQL 实战 45 讲
- MySQL 是怎样运行的:从根儿上理解 MySQL