pyinnodb是一个mysql数据文件的解析工具, 同时支持mysql5.7,8.0以上的磁盘文件解析, 主要特性包括:
-
mysql8.0以上:
- 从ibd文件中导出ddl语句;
- 从ibd文件中导出sql语句;
- 查看ibd文件中的sdi数据;
- 在ibd文件中搜索指定主键的数据;
-
mysql5.7:
- 结合表结构文件(.frm)从数据文件(.ibd)中导出数据;
- 结合表结构文件(.frm)从数据文件(.ibd)中搜索数据;
wget https://github.com/WinChua/pyinnodb/releases/latest/download/pyinnodb.sh
python 3.8 以上
8.0之后, mysql新增了SDI页,将表结构信息和数据都存储在一个文件中,解析的时候只 需要处理但一个.ibd文件即可.
$ ./pyinnodb.sh datadir/test/all_type.ibd validate
page[1], fil.checksum[0x20fa5081], calculate checksum[0x20fa5081], eq[True]
page[2], fil.checksum[0x18395c50], calculate checksum[0x18395c50], eq[True]
page[3], fil.checksum[0x1493810c], calculate checksum[0x1493810c], eq[True]
如输出所示, validate会逐页读取校验字段值,计算页内容的crc32值, 输出比较结果
通过 | grep False
可以确定是否存在某个页数据校验值不等,则表名.ibd存在损坏
$ ./pyinnodb.sh datadir/test/all_type.ibd tosql --mode ddl
8.0之后, mysql新增了一种page用于存储表结构数据,将表结构存储在.ibd文件中,一般 称为SDI,通过以下命令查看表结构的sdi数据
$ ./pyinnodb.sh datadir/test/all_type.ibd tosql --mode sdi
SDI页中每一条记录都是一个JSON串, 可以通过 | jnv
实时查看json数据
$ ./pyinnodb.sh datadir/test/all_type.ibd tosql --mode dump
命令会将ibd文件中每一条记录导出成SQL语句, 通过 > data.sql
$ ./pyinnodb.sh datadir/test/all_type.ibd search --primary-key 1
展开输出以及解释
found: all_type(id=2, BIGINT=98283201, BIT=1, DATETIME=datetime.datetime(2024, 1, 1, 9, 0, 1), DOUBLE=3.1415926, FLOAT=6.189000129699707, INTEGER=8621, LONGBLOB='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', LONGTEXT='ggg', MEDIUMBLOB=None, MEDIUMINT=999999, MEDIUMTEXT=None, NUMERIC=Decimal('11'), REAL=1092.892, SMALLINT=981, TEXT='TEXT', TIME=datetime.timedelta(seconds=11040), TIMESTAMP=datetime.datetime(2024, 7, 24, 9, 5, 28), TINYBLOB='TINYBLOB', TINYINT=99, TINYTEXT='TINYTEXT', YEAR=2024, ENUM=b'a', SET='a,b,c', DECIMAL=Decimal('910.79'), CHAR=None, VARBINARY='VARBINARY', int_def_col=42, str_def_col='world')
search命令通过--primary-key选项指定主键的值, 将会在ibd文件中查找主键等于该值的记录
此外,search命令还包括--hidden-col, 指定后将会解析,记录的隐藏字段, 如:
$ ./pyinnodb.sh datadir/test/all_type.ibd search --primary-key 2 --hidden-col
展开输出以及解释
found: all_type(id=2, BIGINT=98283201, BIT=1, DATETIME=datetime.datetime(2024, 1, 1, 9, 0, 1), DOUBLE=3.1415926, FLOAT=6.189000129699707, INTEGER=8621, LONGBLOB='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', LONGTEXT='ggg', MEDIUMBLOB=None, MEDIUMINT=999999, MEDIUMTEXT=None, NUMERIC=Decimal('11'), REAL=1092.892, SMALLINT=981, TEXT='TEXT', TIME=datetime.timedelta(seconds=11040), TIMESTAMP=datetime.datetime(2024, 7, 24, 9, 5, 28), TINYBLOB='TINYBLOB', TINYINT=99, TINYTEXT='TINYTEXT', YEAR=2024, ENUM=b'a', SET='a,b,c', DECIMAL=Decimal('910.79'), CHAR=None, VARBINARY='VARBINARY', int_def_col=42, str_def_col='world', DB_TRX_ID=2064, DB_ROLL_PTR=MRollbackPointer(insert_flag=1, rollback_seg_id=1, page_number=257, page_offset=350))
DB_ROOL_PTR
以及DB_TRX_ID
如果进一步查看数据的修改记录, 可以指定 --with-hist 以及--datadir指定mysql的数据目录来查看, 如:
$ ./pyinnodb.sh datadir/test/all_type.ibd search --primary-key 2 --hidden-col --with-hist --datadir datadir
展开输出以及解释
found: all_type(id=2, BIGINT=98283201, BIT=1, DATETIME=datetime.datetime(2024, 1, 1, 9, 0, 1), DOUBLE=3.1415926, FLOAT=6.189000129699707, INTEGER=8621, LONGBLOB='xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', LONGTEXT='ojbk', MEDIUMBLOB=None, MEDIUMINT=999999, MEDIUMTEXT=None, NUMERIC=Decimal('1314520'), REAL=1092.892, SMALLINT=981, TEXT='TEXT', TIME=datetime.timedelta(seconds=11040), TIMESTAMP=datetime.datetime(2024, 7, 24, 9, 5, 28), TINYBLOB='TINYBLOB', TINYINT=99, TINYTEXT='TINYTEXT', YEAR=2024, ENUM=b'a', SET='a,b,c', DECIMAL=Decimal('910.79'), CHAR='89', VARBINARY='VARBINARY', int_def_col=42, str_def_col='world', DB_TRX_ID=2073, DB_ROLL_PTR=MRollbackPointer(insert_flag=0, rollback_seg_id=2, page_number=277, page_offset=745))
<Update by trx[2071]: LONGTEXT
updated original value: LONGTEXT AGAIN; NUMERIC
updated original value: 20230304>
<Update by trx[2069]: LONGTEXT
updated original value: None; CHAR
updated original value: None>
<Update by trx[2064]: LONGTEXT
updated original value: ggg; NUMERIC
updated original value: 11>
<Insert>
<Update by trx[2071]: `LONGTEXT` updated original value: LONGTEXT AGAIN; `NUMERIC` updated original value: 20230304>
表明, 该条记录在事务ID[2071]中被修改了, 涉及的字段包括LONGTEXT
(修改前的值为LONGTEXT AGAIN
)以及NUMERIC
(修改前的值为20230304
mysql 5.7的文件组织方式与mysql8.0不同,表结构存储在.frm文件,而数据存储在.ibd,对ibd文件的解析需要使用:
./pyinnodb.sh datadir/test/all_type.ibd frm datadir/test/all_type.frm