MySQL行格式详解
文章目录
- MySQL行格式详解
- 🎉 什么是行格式
- 🐱👤 如何查看行格式
- 🐱🚀 InnoDB 行格式有哪些?
- 🐱🏍 Compact 行格式
- 🚩 额外信息
- 🚀 变长字段长度列表
- 🚓 Null值列表
- 🚁 记录头
- 🌌 真实数据
- 🌍 隐藏字段
- 🗻 存储的真正字段
🎉 什么是行格式
首先我们得明确一点,行格式是存在页中的,数据页中存储的行格式是数据行(也就是实际数据),目录页存的是目录项记录【非叶子节点行】(存储页号和主键)
🐱👤 如何查看行格式
-- 创建数据表时,显示指定行格式
CREATE TABLE 表名 (列的信息) ROW_FORMAT=行格式名称;-- 创建数据表时,修改行格式
ALTER TABLE 表名 ROW_FORMAT=行格式名称;-- 查询数据表信息
show table status from 数据库名 like '<数据表名>';
-- 指定行格式
create table task2
(idx int auto_increment,primary key (idx)
) row_format = compact;-- 查看行格式
show table status from test1 like 'task2';
可以看到下图 Row_format 字段种就是行格式
🐱🚀 InnoDB 行格式有哪些?
InnoDB
提供了四种行格式,分别是Redundant
、Compact
、Dynamic
和 Compressed
特性/格式 | Redundant | Compact | Dynamic | Compressed |
---|---|---|---|---|
存储效率 | 最低,存在冗余 | 中等,行头较小,适合常规数据 | 高效,特别是对于大字段的存储 | 极高,适用于大规模压缩,节省磁盘空间 |
适合场景 | 旧版本兼容,较小数据表 | 默认格式,常规数据库 | 适合大字段存储和需要高压缩的场景 | 大量数据表,磁盘空间有限,压缩需求高 |
行头大小 | 较大,包含多余的标记和指针 | 较小,更紧凑的存储结构 | 较大,包含更多字段指针和溢出页信息 | 行头较大,压缩后存储空间利用较高 |
大字段存储 | 存储在行内或溢出页 | 存储在行内或溢出页 | 存储在外部溢出页,仅保存指针 | 存储在溢出页,且经过压缩处理 |
压缩支持 | 不支持压缩 | 不支持压缩 | 不支持压缩 | 支持压缩,数据写入时自动压缩 |
性能 | 较低,冗余存储带来性能损失 | 性能平衡,适合一般负载 | 性能较好,适合大字段,处理复杂数据表 | 性能较低,解压缩开销较大 |
兼容性 | 主要用于早期 MySQL 版本 | MySQL 5.0 之后推荐的默认行格式 | MySQL 5.0 之后推荐的格式 | 用于对存储空间要求高的数据库 |
- Redundant:不再推荐使用,主要用于与早期版本的兼容。
- Compact:适用于常规数据存储,提供较好的性能和存储效率,是大多数情况下的默认行格式。
- Dynamic:适合大字段、可变长度字段,特别是存储
TEXT
和BLOB
等类型的数据,能够有效提高存储效率。 - Compressed:适用于对存储空间有较高需求,尤其是存储大量数据时,能够显著减少磁盘使用,但可能会引入 CPU 开销。
下面我们主要介绍一下 Compact 行格式
🐱🏍 Compact 行格式
如下图是 Compact 行格式的一个图解
Compact 行格式主要分为几个部分
🚩 额外信息
🚀 变长字段长度列表
变长字段长度列表主要是记录变长字段 varchar 所占的长度大小,举个例子,有如下表
CREATE TABLE `t_user` (`id` int(11) NOT NULL,`name` VARCHAR(20) DEFAULT NULL,`phone` VARCHAR(20) DEFAULT NULL,`age` int(11) DEFAULT NULL,PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB
DEFAULT CHARACTER SET = ascii ROW_FORMAT = COMPACT;
当有一条记录是(1,“a”,“123”,18)的时候,由于使用的是 ASCII 字符集
- name 占 6 个字节,转换成 16 进制就是 0x06
- phone占 3 个字节,转换成 16 进制就是0x03
- age 列和 id 列不是变长字段
这些变长字段的真实数据占用的字节数会按照列的顺序逆序存放(能够提高cpu的cache命中率),那这一条记录变长字段长度列表
存储的值就是 03 06,如图所示
为什么要逆序存放?
答:
- 记录头信息中,指向下一条记录的指针,指向的是记录头信息和真实数据的中间位置,这样可以往左读就是记录头信息,右就是真实数据,比较方便
- 使得靠前的变长字段长度列表和真实数据可以尽量在同一个cpu的cache行中,提高cache的命中率
当表没有变长字段的时候,那么变长字段长度列表就不存在于行格式
🚓 Null值列表
通常情况下 NULL 值列表大小是 1 字节,也就是 8 bit,每个 bit 位表示一个字段
- 二进制位的值为
1
时,代表该列的值为NULL。 - 二进制位的值为
0
时,代表该列的值不为NULL。
Null值列表对应的列也是按照逆序对应,如下图
举个例子,假设现在我有一条记录(1,“a”,“123”,null), id 有 NOTNULL 约束,所以不参与到 null值列表中来;name
不为null,所以对应的是0;同理,phone
对应的也是0;age
字段为null,所以对应的是1;所以字段的顺序对应的null值列表是001
,因为要逆序存放,所以最终空值列表是100
- 当表中所有字段都有
NOT NULL
约束时,那么就不存在 NULL 值列表 - 当表中可以为 NULL 的字段大于 8 个,也就是8 bit不足以存下 Null 值列表的时候, Null 值列表就会扩展到 2 字节(8 bit),以此类推
🚁 记录头
如下图
- delete_mark:行删除标记,在 innodb 中对于行数据的删除并不会马上去刷盘,而是先打上一个标记,待后续刷盘时机到了再把脏页刷入
- min_rec_mark:B+树每层非叶子节点中最小的目录项记录会添加该标记(也就是目录页中主键值最小的记录的min_rec_mark是1,其他是0)。
- record_type:记录类型,0 代表普通记录(记录数据),1 代表非叶子结点页中的记录(目录项记录),2 代表最小记录,3 代表最大记录。
- n_owned:一个页有多条记录,这些记录会再进行分组(一个分组对应一个页的槽位),每个分组有一条记录会存储该分组有多少条数据,这个字段就是用来标记该分组有多少条记录的。
- heap_no:当前记录在页中相对位置。
- next_record:指向下一条记录的指针,这里可以对应到 B+树的结构特点。
🌌 真实数据
上图是 compact 行格式中记录的真实信息除了几个列值外,还有三个隐藏字段
🌍 隐藏字段
- row_id:如果建表时指定了主键或者唯一约束,如果没有指定,那么 InnoDB 就会为记录添加 row_id 隐藏字段作为聚簇索引的键。,row_id 不是必须的
- trx_id:事务id,表示这个数据是由哪个事务生成的,trx_id是必需的
- roll_ptr:这条记录上一个版本的指针。roll_pointer 是必需的
🗻 存储的真正字段
按照顺序排放,null值字段被省略。