一、Redundant 行记录格式
在 MySQL 5.0 版本之前,Innodb 采用 Redundant 这种行记录存储方式。MySQL 之所以仍保留对 Redundant 的支持,关键在于要确保与旧版本页格式的兼容性,以此维系数据库在不同版本更迭期间的平稳过渡与数据连贯性。值得一提的是,Redundant 行记录格式自身还具备不少独特优点,使其在当时发挥了重要作用。
其一,Redundant 格式有着出色的稳定性。在早期 MySQL 数据库应用场景尚不算复杂、硬件资源相对有限的环境下,它能够稳健地承载各类数据存储任务,极少出现因存储格式自身问题引发的数据异常或丢失情况,为数据库的稳定运行筑牢根基,让企业用户放心地将关键业务数据托付其中。
其二,数据解析简易。从存储结构来看,虽说它与 Compact 行记录格式有所不同,但正是因为自身记录头信息及固定的存储规则,让数据解析环节相对直截了当。开发人员、数据库管理员在进行数据提取、排查问题或是执行低层级的数据操作时,能够迅速依据其格式特点定位到目标数据,节省大量时间成本,提升运维与开发效率。
其三,Redundant 行记录格式兼容性极佳。它不仅适配旧版本 MySQL 的诸多特性与功能模块,对于一些老旧的第三方数据库工具、插件,同样能够无缝对接。这意味着企业无需大规模更换周边技术生态,就能持续利用既有资源开展业务,降低技术升级的成本与风险。
再看 Redundant 行记录格式的具体构成,它与广为人知的 Compact 行记录格式相比,二者有着显著差异。Compact 行记录格式的开头部分是字段长度偏移列表,该列表别具一格,依照列顺序的逆序进行排列。具体而言,若字段长度小于 255 字节,仅用 1 字节予以表示;一旦长度超出 255 字节,便会启用 2 字节来精准描述。
第二部分是记录头信息,这部分稳稳占据 6 字节的存储空间。其中,n_fields 值是重中之重,它精准记录了一行当中列的数量,以 10 位二进制数来承载信息,理论上最大值可达 1023,这一数值设定恰好揭示了 MySQL 数据库每行最多支持 1023 个列的底层缘由;另有一个 1byte_offs_flag,该标识肩负特殊使命,专门用于界定偏移列表究竟占用 1 字节还是 2 字节,为后续数据读取、解析提供关键指引。下面是记录头信息具体解析:
名称 | 大小 | 描述 |
---|---|---|
() | 1 | 未知 |
() | 1 | 未知 |
deleted_flag | 1 | 该行是否已被删除 |
min_rec_flag | 1 | 存储目录项记录中主键值最小的目录项记录置为1,其它情况都置0. |
n_owned | 4 | 页目录中每个组的最后一条记录会存储该组的记录数,作为n_owned 字段。值的关注的是,在mysql中最小记录是一组,普通记录与其它记录是一组,因此最小记录中n_owned属性是1,最大记录的n_owned值是5. |
heap_no | 13 | 当前页中该记录的排序位置 |
n_fields | 10 | 记录中页的数量 |
1byte_offs_flag | 1 | 偏移列表为 1 字节还是 2 字节 |
next_record | 16 | 页中 下一条记录的相对位置 |
total | 40 | 合计 |
二、实践
1. 创建表结构并导入对应数据
# mytest 表结构
mysql> show create table mytest \G;
*************************** 1. row ***************************
Table: mytest
Create Table: CREATE TABLE `mytest` (
`t1` varchar(10) DEFAULT NULL,
`t2` varchar(10) DEFAULT NULL,
`t3` char(10) DEFAULT NULL,
`t4` varchar(10) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=COMPACT
1 row in set (0.00 sec)
# 根据 mytest 表 创建表结构
create table mytest2 engine = Innodb row_format = redundant
as select * from mytest
# 表定义
mysql> show table status like 'mytest2' \G;
*************************** 1. row ***************************
Name: mytest2
Engine: InnoDB
Version: 10
Row_format: Redundant
Rows: 8
Avg_row_length: 2048
Data_length: 16384
Max_data_length: 0
Index_length: 0
Data_free: 0
Auto_increment: NULL
Create_time: 2024-12-09 10:32:41
Update_time: 2024-12-09 10:32:41
Check_time: NULL
Collation: utf8mb4_general_ci
Checksum: NULL
Create_options: row_format=REDUNDANT
Comment:
1 row in set (0.00 sec)
2. 找到 mytest2.ibd 文件,并使用二进制文件打开
# 查找到 C07D位置
23 20 16 14 13 0c 06 # 边长字段列表
00 00 10 0f 00 ba # record header
00 00 00 00 02 09 # rowid
00 00 00 00 05 5f # transactionID
da 00 00 01 59 01 10 # roll point
61
62 62
62 62 20 20 20 20 20 20 20 20
63 63 63
# 解析第一行 变长字段列表 23 20 16 14 13 0c 06
# 转换为 06 0c 13 14 16 20 23 分别代表:
# 第一列长度 6,第二列长度 6 = 0x0C - 0x06 第三列长度 7 = 0x13 - 0x0C
# 第四列长度 1 = 0x14 - 0x13,第五列长度 2 = 0x16 - 0x14
# 第六列长度 10 = 0x20 - 0x16,第七列长度 3 = 0x23 - 0x20
# 解析第二行
# 00 转换为 二进制 0000 0000,前两位不介绍,第三位表示当前记录没有被删除, 第四位表示当前记录不是最小记录,
# 后四位表示 当前组有多少数据,记录在最后一行,当前不是最后一行数据,所以为 0
# 00 10 0f 转换为二进制 为 0000 0000 0001 0000 0000 1111
# 对于前 13 位 0000 0000 0001 0,表示排序顺序为 10
# 对于 14 到 23 位 000 0000 111 记录列的数量,转换为 10进制为 7 位,正好对应边长字段个数
# 最后 1 为 是否为 1 字节,当前为 1,表示列的长度用 1 字节表示
# 0c 06 表示下一行的相对位置 0xC089 + 0x00BA - 0x008A (当前数据的偏移地址) = 0xC0B9
# 后面 7 列为正常数据列 其中 3 行为隐藏列,4 行为定义数据列
# 第二行数据
23 20 16 14 13 0c 06 # 边长字段列表
00 00 18 0f 00 ea # record header
00 00 00 00 02 0a # rowid
00 00 00 00 05 5f # transactionID
da 00 00 01 59 01 1e # roll point
# transactionID 为什么和第一条数据一样
# 因为 他们是在一个事物提交的, 所以事务是一样的
# 注意 第三条数据的位置计算 0xC0B9 + (0x00EA - 0x00BA (当前数据的偏移地址)) = 0xC0B9 + 0x30 = C0E9
# 第三行数据 null 数据
21 9e 94 14 13 0c 06 # 边长字段列表
00 00 20 0f 01 18 # record header
00 00 00 00 02 0b # rowid
00 00 00 00 05 5f # transactionID
da 00 00 01 59 01 2c # roll point
64
00 00 00 00 00 00 00 00 00 00
66 66 66
# 解析第一行 变长字段列表 21 9e 94 14 13 0c 06
# 转换为 06 0c 13 14 94 9e 21 分别代表:
# 第一列长度 6,
# 第二列长度 6 = 0x0C - 0x06
# 第三列长度 7 = 0x13 - 0x0C
# 第四列长度 1 = 0x14 - 0x13,
# 第五列固定长度 null 变成 0x94,边长列表记录为 0x94 固定写死,行中数据使用00占用
# 第六列长度 10 = 0x9e - 0x94,记录当前行占用多少字节,但是具体行不存储数据
# 第七列长度 3 = 0x21 - 0x14 - 0x0A(第二行数据 null占用字节 )
当前表 mytest2 的字符集为 Latin1,每个字符最多占用 1 字节,若用户将表的字符集改为 utf8,第三列固定长度类型长度不再是占用10字节,而是 10 * 3 = 30 字节, 所以在 Redundant 下 char 可能是占用存储最大字节数。
标签:10,00,20,字节,记录,笔记,Innodb,Redundant,长度 From: https://blog.csdn.net/weixin_43262384/article/details/144338550