日常运维中, 难免遇到某些表不小心被drop的场景, 而恰好又没有备份? 咋办呢? 当然是跑路啦
undrop-for-innodb 是一款很NB的数据恢复工具,支持在没有备份的时候恢复被drop的表; 网上教程也很多, 但是都是针对5.6/5.7环境的, 其实这款工具也是支持MySQL 8.0环境的. 本文主要就是讲如何使用undrop-for-innodb和ibd2sql恢复mysql 8.0环境被drop的表
先来看看undrop-for-innodb的原理:
stream_parser是扫描磁盘提取索引页的工具,支持使用
-T
指定要解析的索引
看起来比较啰嗦, 总结就是:解析ibdata1获取indexid,根据indexid扫描磁盘获取数据.
为了方便展示, 我们画个图:
而8.0里面的表/索引信息是放在mysql.ibd中的, 表结构信息还可以找开发提供, 而indexid部分就无法使用undrop-for-innodb了. 咋办呢?
有一款名叫ibd2sql的工具可以解析ibd文件, mysql.ibd也是ibd文件, 那不就可以解析了么!
ibd2sql解析ibd文件的原理其实就是: 根据表结构一行行,一个个字段的读取ibd文件中的数据. 当然可以使用一些特殊选项来解析需要的数据.
所以我们可以把获取索引id之类的信息交给ibd2sql做, 抽取索引页的步骤给undrop-for-innodb做, 解析数据部分,两个都可以做.
目前ibd2sql不支持根据系统表(mysql.tables,mysql.columns等)来拼接DDL, 所以DDL部分我们还得找开发要. 我这里就直接给出DDL了
undrop-for-innodb的下载安装及编译(需要先安装好mysql)
# 软件下载
wget https://github.com/twindb/undrop-for-innodb/archive/refs/heads/master.zip
unzip master.zip
cd undrop-for-innodb-master
# 环境准备
yum install make gcc -y
# 编译
make
# 输出stream_parser,c_parser,innochecksum_changer 在当前目录
# 编译sys_parser (需要安装mysql, 即需要使用mysql_config)
make sys_parser
# 输出 sys_parser
# 需要libmysqlclient
ln -s /soft/mysql_3306/mysqlbase/mysql/lib/libmysqlclient.so.20 /usr/lib64/mysql/libmysqlclient.so.20
ldconfig
ibd2sql的下载
yum install python3 -y
wget https://github.com/ddcw/ibd2sql/archive/refs/heads/ibd2sql-v2.x.zip
unzip ibd2sql-v2.x.zip
cd ibd2sql-ibd2sql-v2.x/
数据库的安装就省略了.
本次使用8.0.43版本模拟.
select @@version;
create table t20250913_test_drop(id int, name varchar(200));
insert into t20250913_test_drop values(1,'ddcw'),(2,'https://github.com/ddcw/ibd2sql'),(3,'https://github.com/twindb/undrop-for-innodb');
drop table t20250913_test_drop;
先根据表名获取tableid
python3 main.py /data/mysql_3306/mysqldata/mysql.ibd --sql --delete --complete --set table=tables | grep t20250913_test_drop
这个id字段就是tableid, 故tableid=370
根据tableid获取主键的索引id
python3 main.py /data/mysql_3306/mysqldata/mysql.ibd --sql --delete --complete --set table=indexes | grep 370
table_id为370对应的数据有个 "id=159;root=4..." 中id=159的159就是indexid; root=4中的4是root pageno, 但都被drop了, 就无法根据pageno来恢复了. 所以我们只需要indexid.
./stream_parser -f /dev/vda1 -t 50G -T 159
注意不一定能扫描出来的, 有可能被回收了, 全看运气, 所以备份是很重要的.
然后我们就可以使用ibd2sql解析相关数据页的数据了.
得需要相同表结构的frm/ibd文件, 且需要修改后缀.page为.ibdpython3 main.py /root/undrop-for-innodb-master/pages-vda1/FIL_PAGE_INDEX/0000000000000159.ibd --sdi /data/mysql_3306/mysqldata/db2/t20250913_test_drop.ibd --sql --set leafno=0 --set rootno=0 --force
这里解析出来数据是重复的, 是因为undrop-for-innodb扫描的页就是重复的导致的.
需要构造相关的表结构
-- t20250913_test_drop.sql
create table t20250913_test_drop(
id int,
name varchar(200));
然后解析数据
./c_parser -6f pages-vda1/FIL_PAGE_INDEX/0000000000000159.page -t t20250913_test_drop.sql
思考1:
ibd2sql v1.x版本的时候也是支持恢复drop的表的,但原理是解析xfs文件系统, 根据Inode记录的信息去对应的磁盘构建完整的ibd文件. 这种设计限制太大了,如果inode被破坏了, 就无法恢复了. 所以ibd2sql v2.x后续版本也会根据indexid扫描磁盘文件然后尽可能的恢复数据.
对于drop的表的恢复, 和undrop-for-innodb的设计比起来,ibd2sql还是太'老实'了, 不够创新,不够胆大.
思考2:
数据是放在索引页里面的, 索引页是有索引id的; 但是对于溢出数据而言是放在溢出页(off-page)的,而溢出页是不记录索引id的, 那么应该怎么解析呢?
undrop-for-innodb的做法是每个溢出页都放在pages-vda1/FIL_PAGE_TYPE_BLOB目录下面, 解析的时候需要哪个page就去读哪个page. 但如果存在相同的pageid的溢出页时会怎么样呢?
FIL_PAGE_INDEX 目录的文件是根据indexid来的, FIL_PAGE_TYPE_BLOB目录下的文件是根据pageid来的
准备使ibd2sql支持解析undrop-for-innodb碎片页的时候, 看了下undrop-for-innodb的原理, 发现其还不支持8.0环境, 故尝试使其与ibd2sql结合的效果, 还不错. 后续ibd2sql也会引进该功能(不再傻兮兮的解析文件系统了...)
根据indexid,spaceid直接扫描磁盘,然后尽可能的还原为ibd文件, 可能实现起来更简单点. 但如果目标文件很大的话, 还原出来的ibd文件也可能很大了, 即使实际数据并不算多. 道阻且长.
备份很重要!
参考:
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。