[ibd2sql] mysql做过online ddl(instant)的数据应该怎么解析?

2024年 7月 9日 63.1k 0

ibd2sql的诞生过程

  1. 最开始解析ibd文件的时候, 只是一个脚本, 方便了解ibd文件的结构的:MYSQL INNODB ibd文件详解 (1)-腾讯云开发者社区-腾讯云 (tencent.com)

  2. 既然都能解析结构了, 那就顺便提取下数据(ddl+dml):MYSQL INNODB ibd文件详解 (2) 提取DDL和DML-腾讯云开发者社区-腾讯云 (tencent.com) 这时候还只是3个脚本而已.

  3. 接着再更新下元数据信息:MYSQL INNODB ibd文件详解 (3) FIL_PAGE_SDI-腾讯云开发者社区-腾讯云 (tencent.com)

  4. v0.1 信息都解析得差不多了, 那就整一套完整的工具吧. 能解析基础的数据类型就行.(这时候只支持基础数据类型):使用ibd2sql解析ibd文件生成 DDL和DML-腾讯云开发者社区-腾讯云 (tencent.com)

  5. v0.2 然后支持了更多的数据类型

  6. v0.3 对于从5.7升级上来的库, sdi page不是固定的位置, 而是记录在第一页里面的:
    mysql 寻找SDI PAGE-腾讯云开发者社区-腾讯云 (tencent.com)

  7. v1.0 增加了debug功能, 主要是方便调试的:[ibd2sql] ibd2sql v1.0 发布 & ibd文件结构说明-腾讯云开发者社区-腾讯云 (tencent.com)

  8. v1.1 修复一些BUG v1.2支持空间坐标, 也算是完善了数据类型, v1.3 支持mysql 5.x v1.4 支持溢出页和子分区. 基本上算是全部支持了.

  9. v1.5 对做过ONLINE DDL的字段重新做了个解析,(之前遇到的相关BUG,都是临时修一下.), 也是本文的重点.
    项目地址: https://github.com/ddcw/ibd2sql

简介

我们知道mysql在8.0.12引入了INSTANT(online ddl),可以快速的插入列.
在8.0.29 完善了INSTANT(使用row version flag代替instant flag), 使其支持drop列.
原理比较好理解, 就是只修改元数据信息. 读取数据的时候, 根据元数据信息判断改字段是否做过online ddl.
最开始只有个instant flag, 用来标记该行数据是否是在online ddl之后更新的数据(若为online ddl之后操作的, 则存在完整的数据, 若为online ddl之前操作的,则读取字段的默认值, nullable也不考虑该字段.)

该方式使用太有限了, 毕竟只支持快速add column, 如果要删除某一列, 还是得重建表. 于是在8.0.29又引入了row version, 用来标记该行数据的版本, 元数据里面也记录数据行版本. 如果行数据里面的row version比元数据里面的版本信息, 则行数据记录更完整(要考虑nullable和数据). 文字描述还是比较枯燥的, 我们来使用图和代码表示吧.

instant实现逻辑

先介绍几个概念:

对象 描述
INSTANT FLAG 标记是否存在instant 在record第1bit位置, 仅存在于8.0.12-8.0.28版本中
ROW VERSION FLAG 标记是否存在row version 在record第2bit位置, 存在于8.0.29及其以上版本
COLUMN COUNT 字段数量, 当存在INSTANT FLAG时存在, 使用1-2字节表示. 位于null bitmask和record header之间. 包含rowid,trx,rollptr.
ROW VERSION 数据行版本, 当存在ROW VERSION FLAG时存在, 使用1字节表示. 每次add/drop时+1, 最大64. 用来记录该行数据是否为最新的
VERSION_DROPPED 元数据中记录的该字段被删除后的版本
VERSION_ADDED 元数据中记录的该字段添加后的版本

[ibd2sql] mysql做过online ddl(instant)的数据应该怎么解析?-1

代码逻辑参考

# ROW VERSION/COLUMN_COUNT判断
if self.table.mysqld_version_id =80012 and rheader.instant_flag:
_COLUMN_COUNT = self._read_innodb_varsize()
elif if rheader.row_version_flag:
ROW_VERSION = self._read_innodb_varsize()

# NULL BITMASK计算
for _phno,colno in self.table.column_ph:
col = self.table.column[colno]
if rheader.row_version_flag:
if (ROW_VERSION >= col['version_added'] and (col['version_dropped'] == 0 or col['version_dropped'] > ROW_VERSION)) or (col['version_dropped'] > ROW_VERSION and ROW_VERSION >= col['version_added']):
null_bitmask_count += 1 if col['is_nullable'] else 0
else:
null_bitmask_count += 1 if col['is_nullable'] else 0

# 读取索引(注意前缀索引问题)
if self.haveindex: #有索引的时候
for colno,prefix_key,order in self.table.index[self.idxno]['element_col']:
col = self.table.column[colno]
self.debug(f"\tREAD KEY COLNO:{colno} NAME:{col['name']}")

#pk 不需要判断null
if prefix_key == 0:
_data[colno],_expage[colno] = self._read_field(col)
col_count -= 1
self.debug(f"\tREAD KEY NO:{colno} NAME:{col['name']} FINISH. VALUES: {_data[colno]}")
else:
_,__ = self._read_field(col)
self.debug(f'前缀索引数据为: {_} . SKIP IT.' )

else:
self.debug("\tNO CLUSTER KEY.WILL READ 6 bytes(ROWID)",)
_row_id = self._read_uint(6) #ROW_ID
self.debug(f"\t ROW_ID:{_row_id}")

# 读取TRX&ROLLPTR
_row['trx'] = self._read_uint(6)
_row['rollptr'] = self._read_uint(7)

# 读取数据. 存在多种条件
for _phno,colno in self.table.column_ph:
if colno in _data: # KEY
continue
col = self.table.column[colno]
self.debug(f"\tNAME: {col['name']} VERSION_ADDED:{col['version_added']} VERSION_DROPED:{col['version_dropped']} COL_INSTANT:{col['instant']} ROW VERSION:{ROW_VERSION}")
if rheader.row_version_flag: # >=8.0.29 的online ddl
if (ROW_VERSION >= col['version_added'] and (col['version_dropped'] == 0 or col['version_dropped'] > ROW_VERSION)) or (col['version_dropped'] > ROW_VERSION and ROW_VERSION >= col['version_added']):
if col['is_nullable']:
_nullable_count += 1
if col['is_nullable'] and null_bitmask&(1 0: # 删除的字段就不读了(无instant情况下)
_data[colno],_expage[colno] = None,None
self.debug(f"######## DDCW FLAG 6 ########")
#elif col['instant'] and rheader.instant_flag:
else:
#self.debug(f"NULLABLE {col['name']}: is_nullable:{col['is_nullable']} null_bitmask:{null_bitmask} _nullable_count:{_nullable_count}")
if col['is_nullable']:
_nullable_count += 1
if col['is_nullable'] and null_bitmask&(1

相关文章

Oracle如何使用授予和撤销权限的语法和示例
Awesome Project: 探索 MatrixOrigin 云原生分布式数据库
下载丨66页PDF,云和恩墨技术通讯(2024年7月刊)
社区版oceanbase安装
Oracle 导出CSV工具-sqluldr2
ETL数据集成丨快速将MySQL数据迁移至Doris数据库

发布评论