在整理innodb数据存储格式的时候, 发现有位大佬的博客 中说: 主键非叶子节点 不需要记录null bitmask.

按道理来讲, 确实是不需要(毕竟主键的非叶子节点只有主键值, 主键值一定是非空, 那就没必要记录null bitmask了), 但我们应该以实际为主(设计的时候可能没有考虑这些).
我们实际测试出来是有记录null bitmask的. 测试版本范围5.0 --> 8.0,主键非叶子节点均存在null bitmask.
Mysql安装过程就不写了, 需要注意的是要在配置文件加上innodb_file_per_table=1, 老版本的该参数默认是0
这里得写1, 而不能写为on
可使用如下脚本快速生成测试数据:
DDL = "drop table if exists db1.t20250826_ddl;create table db1.t20250826_ddl(aa varchar(20) ,bb varchar(20),cc varchar(20), dd varchar(20),ee varchar(20), ff varchar(20), gg varchar(20), hh varchar(20),ii varchar(20), primary key(aa(19))) engine=innodb;insert into db1.t20250826_ddl values('test r0',1,2,3,4,5,6,7,8);"
print(DDL)
for i in range(10000):
print(f"insert into db1.t20250826_ddl values('tt{i}',{','.join([ str(x) for x in range(8)]) });")然后我们可使用如下脚本快速解析数据文件, 5.x版本没有sdi, 所以主键的root page是在pageid=3的位置.
f = open('/data/mysql5096/data/data/db1/t20250826_ddl.ibd','rb')
f.seek(16384*3,0)
data = f.read(16384)
data[64:66]
import struct
offset = struct.unpack('>h',data[97:99])[0]+99
data[offset-7:offset]
data[offset:][:20]
于是呢, 我们就稍微整理了下innodb的数据结构.

主键里面记录了完整的字段信息的, 所以得单独考虑(而且还有instant算法在里面..)




上面出现了一堆的名称, 我们来一一解释.
varsize: 对于变长字段(varchar,text,部分char)需要使用1-2字节来记录数据的实际存储长度. 若字段最大字节超过255, 则使用1-2字节(看第一字节是否达到128); 若字段最大字节不超过255, 则使用1字节存储即可.
null bitmask: 有些字段是没有非空约束的, 即可为空, 那么就需要使用1bit来记录其是否为空. 若为空,则varsize部分也不需要记录该字段了.
row_count: 如果是8.0.28及其之前的版本使用Instant算法增加字段时, 则之后新插入的数据行需要记录数据行数, 这样才知道这行数据的时候有多少个字段,不然使用instant多加几个字段就分不清这行数据有多少字段了. 使用1-2字节来记录, 和varsize一样的计算方式.
这里有个坑, 即instant增加字段时,不考虑字段长度是否达到限制, 也就导致有些表的行长度超过65535了...
row_version: 如果是8.0.28之后的版本使用instant加减字段时, 之后的数据行会使用1字节来记录row_version, 每次加减字段,则row_version值加1, 这样一看row_version,就知道这行数据加了多少字段,删了多少字段
record header: 一些字段头信息, 不同的row_format格式不一样. compact和dynamic格式如下:
对象 | 大小 | 描述 |
|---|---|---|
REC_INFO_INSTANT | 01 bit | 8.0.12-28是否使用add column instant |
REC_INFO_VERSION | 01 bit |
|
REC_INFO_DELETED | 01 bit | 这一行数据是否被删除了 |
REC_INFO_MIN_REC | 01 bit | 这一行数据是否是最小记录(non-leaf) |
REC_N_OWNED | 04 bit | 这个slot有多少行数据(4-8) |
REC_HEAP_NO | 13 bit | heap no |
REC_STATUS | 03 bit | REC_TYPE, 字段类型 |
REC_NEXT | 16 bit | 下一个record的相对位置, 是有符号的. |
redundant的格式如下:
对象 | 大小 | 描述 |
|---|---|---|
REC_INFO_INSTANT | 01 bit | 8.0.12-28是否使用add column instant |
REC_INFO_VERSION | 01 bit |
|
REC_INFO_DELETED | 01 bit | 这一行数据是否被删除了 |
REC_INFO_MIN_REC | 01 bit | 这一行数据是否是最小记录(non-leaf) |
REC_N_OWNED | 04 bit | 这个slot有多少行数据(4-8) |
REC_HEAP_NO | 13 bit | heap no |
REC_N_FIELDS | 10 bit | 有多少个字段 |
REC_SHORT | 01 bit | 是否使用1字节存储长度 |
REC_NEXT | 16 bit | 下一个record位置 |
compressed比较特殊, 可参考之前写的compressed的格式的文章
primary key和key: 表示该索引的值, 如果是前缀索引,则只记录前缀部分.
child pageid: 记录的子叶的pageid, 子叶可能是叶子节点, 也可能是非叶子节点.
(pk) the rest of field: 记录剩下的字段信息, 如果是主键是前缀索引,这里还记录完整的主键值.
mysql的叶子节点和非叶子节点, 主键索引和非主键索引均会记录null bitmask. 有的小伙伴可能会好奇, 主键的非叶子节点的字段都不为空啊, 那这个null bitmask是记录的谁的呢? 测试发现是记录的剩余字段的.但由于非叶子节点不存储剩余字段信息,所以这个null bitmask也就一直是0. (虽然一直是0,但也得记录).
我们只测试到了5.0版本, 并不能说更早之前的版本的存储格式.
参考:
https://blog.jcole.us/2013/01/10/the-physical-structure-of-records-in-innodb/
ibd2sql 2.x版本已经写好了(都TM写了1年了...),进入测试阶段了. 主要是优化了性能,扩展了支持范围,也支持解析多个文件,也支持并发.

web界面主要是加了个返回上页的功能


原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。