暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

[MYSQL] frm2sdi(3) 将frm文件转为sdi page & ibd2sql v1.9 出土

原创 大大刺猬 2025-02-07
292

导读

终于来到frm2sdi的最后一章, 本章将如何将frm转为sdi. 前面两张分别将了frm的结构和sdi的结构. 但frm有部分元数据信息没有细看, 主要是太复杂了, 所以挪到本章来讲. 待我们将frm转为sdi之后, 我们就可以使用ibd2sql来直接解析mysql 5.7的文件了, 不在需要转到8.0里面去了.

最终效果大概如下:
image.png

decimal的0开头懒得去掉了, 毕竟这只是一个过渡版本.

frm2sdi

元数据信息

frm部分信息, 目前主要还剩metadata那17字节. 里面包含的信息非常丰富. 主要是各种精度,是否空之类的元数据信息.

对象 大小 描述
1
1
1
field_length 2 字段长度, 比如int(11), varchar(200)
recpos 3
pack_flag 2 比如是否为空.zero fill,signed
unireg_type 1 生成列, 自增列之类的信息
charset_low 1 字符集的高位
interval_nr 1
field_type 1 字段类型
collation_id 1 字符集 如果是geom的话 ,就是geom类型
comment_length 2 注释长度

前面3字节都是一些信息的一部分(毕竟1字节存不了多少, 有兴趣的可以自己研究)

我们不直接看各字段所代表的意思, 毕竟有些是多个字段合起来才能得到一个有效信息.

分区信息这种已经是字符串形式的值了, 我就不再讲了

字符串长度

对于varchar而言, 字符长度是field_length除以字符集最大字节数.(上一章讲过).
元数据里面记录的是排序规则(collate_id), 所以我们得先查询排序规则对应的字符集, 然后再查询该字符集的最大字符长度. (utf8mb3 就是utf8 most bytes 3, 所以最大字节是3 utf8mb4就是utf8 most bytes 4,所以最大字节就是4). 记不住也没事, 可以数据库里面查询.

-- 查询collation_id=33的字符集 show collation where id=33; -- 查询utf8字符集的最大字节长度 show charset where Charset='utf8';

image.png

数字精度

对于decimal的精度是来自field_length和pack_flag
numeric_precision 即字符长度(整数+小数,不含小数点), 为field_length -2
numeric_scale 为小数部分长度,是pack_flag的前1字节(注意是小端)的后4bit.代码逻辑参考:

if col['metadata']['field_type'] == 246: # decimal numeric_precision = char_length - 2 numeric_scale = (pack_flag>>8) & 31 numeric_scale_null = False

时间精度

时间字段存在精度, 比如毫秒,微秒之类的.
对于timestamp/datetime而言, datetime_precision = 字符长度 - 20
对于time而言(没得年月日), datetime_precision = 字符长度 - 11

enum和set

enum和set会在options中记录 枚举/列表的数量:interval_count.

if field_type == 22: # enum 重新计算type_default_size type_default_size = 2 if len(col['elements']) >= 2**8 else 1 options = f'interval_count={len(col["elements"])}' elif field_type == 23: # set type_default_size = (len(col['elements'])+7)//8 options = f'interval_count={len(col["elements"])}

默认值

默认值分为: 是否有默认值 和 默认值.

前面我们讲的默认值结构有点问题, flag部分不是定长的, 而是一个bitmask, 用来表示某个字段是否有默认值, 而不管是否有默认值, DEFAULT_VALUE部分均会保留足够大的空间(varchar(200) default ‘aa’, 则是b’aa’后面跟上598个b’\x00’). 对于这种使用bitmask来表示是否为空(有默认值)的方式在行存储的时候也会使用到.

如果存在pack_record, 则bitmask从第一个bit位置开始计算, 否则从第二个bit开始计算.

null_bitmask_adds = 0 if self.pack_record == 1 else 1 default_value_null = False if self.default_value_null_bitmask&(1<<(i+null_bitmask_adds)) == 0 else True

对于定长类型,DEFAULT_VALUE部分直接记录值, 对于变长类型, 则会使用1-2字节记录长度, 随后记录数据, 不够的填充b’\x00’. 至于使用1/2字节则看char_length. (char_length<=255,则使用1字节). 这里的数据类型是mysql的数据类型(小端), 和innodb(大端)有区别. 计算默认值的时候, 还得考虑精度.

元数据中的"recpos-1"即表示该字段位于DEFAULT_VALUE(bitmask)的起始位置, 结束位置是下一个字段的开始位置.

是否为空

字段非空约束则是来自 pack_flag&32768

是否有符号

对于int类型还存在符号问题, 虽然我们判断char_length的值也能知道符号, 但pack_flag&1 更直接一点

是否填充0

对于int之类的还存在是否填充0,使其更美观的参数, 来自:pack_flag&4

是否自增

对于字段是否是自增的, 则看unireg_type==15

隐藏字段

mysql的表还存在trxid和rollptr的隐藏字段, 如果没得主键,还有rowid, 这部分信息在转换的时候也得考虑上. 我这里就只填充点关键信息

# 补充隐藏字段 rowid,rollptr & trxid if not self.HAVE_PRIMARY: current_ordinal_position += 1 COLUMN.append({ 'name':'DB_ROW_ID', 'type':10, 'hidden':2, 'char_length':6, 'ordinal_position':current_ordinal_position, }) current_ordinal_position += 1 COLUMN.append({ 'name':'DB_TRX_ID', 'type':10, 'hidden':2, 'char_length':6, 'ordinal_position':current_ordinal_position, }) current_ordinal_position += 1 COLUMN.append({ 'name':'DB_ROLL_PTR', 'type':9, 'hidden':2, 'char_length':7, 'ordinal_position':current_ordinal_position, })

索引补充

如果没得主键, 就得补充主键. 构建的主键,前3个是rowid,trxid,rollptr,然后才是普通字段.
如果有主键的话, 还得再补充下trxid和rollptr,然后再补充下剩余字段(还得考虑是否是前缀索引, 我这里就先不管了…).
普通索引的话, 得补充下主键字段.

ibd2sql v1.9

我只是抽取了部分比较关键的信息讲了, 感兴趣的可以直接看ibd2sql中的相关代码. v1.9版本主要是修复一个已知BUG支持直接解析5.7的ibd文件, 即自动解析frm文件并构造一个sdi page. 还是直接瞧瞧效果吧.(用法也没变)

python3 main.py /data/mysql_3314/mysqldata/ibd2sql/ddcw_alltype_table.ibd --sql --limit 1

image.png
3308实例是5.7.38环境, 3314实例是8.0.28环境.

当然提取DDL也是可以的.

python3 main.py /data/mysql_3308/mysqldata/ibd2sql/ddcw_alltype_table.ibd --ddl

image.png
虽然看着比较怪, 明明是解析frm文件, 却指定的是ibd文件…

总结

frm主要是信息比较紧凑, 而且不容易扩展. sdi就是压缩的json数据(普通数据行), 长度可达4GB?

对于直接解析mysql 5.7环境的frm的前提条件: 要求同目录下存在同名的.frm文件.(虽然我去做自动识别了, 但起码得有啊)

ibd2sql v1.9下载地址: https://github.com/ddcw/ibd2sql/archive/refs/tags/v1.9.tar.gz

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论