导读
ibd2sql v2.2版本新增了恢复drop和truncate的表的功能. 这么叼? 我来瞅瞅呢
原理
虽然之前讲过原理, 但还是来简单回顾回顾:
DROP TABLE是delete和insert系统表, 那么被删表之前的元数据信息就能找到, 虽然数据文件没了,但磁盘不会马上覆盖, 我们就能拿着元数据信息去磁盘找相关的PAGE.
TRUNCATE TABLE 是对系统表做的update,看不到被删表之前的元数据信息了; 5.7之前和现在元数据信息一样, 无所谓, 8.0的话就只能使用排除法了, 即排除有主的page,剩下的再一个个试.
可能有很多小伙伴关心: 怎么判断是需要的page呢?
从磁盘读N个块,然后判断是否是page,是否是index page,是否页校验通过… 大概如下:
现在来看看ibd2sql该怎么使用才能恢复数据
流程就是上面的从系统表提取元数据信息, 然后再扫描磁盘匹配即可. 当然每一步都是可以拆开的.
看着头都大了. 我们直接实践吧.
5.7的元数据文件是ibdata1, 8.0是mysql.ibd, 请自己根据自己的版本做替换, 演示例子可能会交换着来.
恢复被drop的表
实际使用可能需要考虑很多情况, 我这里简单列出一些场景.
这里使用的是5.7的环境; 如果是你是mysql 8.0环境, 请将 ibdata1换成mysql.ibd 其它不变
场景1: 直接恢复
不小心drop了一张表, 啥也不管,直接干.
-- 查看表数据,方便验证
select * from t20260108_101;
-- 直接drop
drop table t20260108_101;
然后我们扫描磁盘恢复被删除的数据:
python3 main.py /data/mysql_5744/mysqldata/ibdata1 --scan /dev/vdb --sql
是不是嘎嘎简单?
场景2 存在多张表被drop的时候
实际中, 可能有多张表被删除, 我们需要先筛选一番
python3 main.py /data/mysql_5744/mysqldata/ibdata1 --scan --ddl
这里有2张表, 我们可以使用--set table来指定需要的表, 并且我们不要直接输出sql, 先输出为page (不要–sql就是输出page形式,也就是二进制文件)
python3 main.py /data/mysql_5744/mysqldata/ibdata1 --scan /dev/vdb --set table=t20260108_101
然后我们再扫描上面获取的目录
python3 main.py /data/mysql_5744/mysqldata/ibdata1 --scan ./ibd2sql_auto_dir_20260108_155924 --sql
场景3 全都要
磁盘下所有的index page我全都要, 主要是怕万一被覆盖了.
python3 main.py --scan /dev/vdb --set indexid=0,all --parallel 8
然后再自己去找需要的page并解析. 比如我要解析ibd2sql_auto_dir_20260108_160541/index/0_0000000037_0000000000000058.page则可以使用
python3 main.py ibd2sql_auto_dir_20260108_160541/index/0_0000000037_0000000000000058.page --sdi /data2/db1/t20260108_101.ibd --set leafno=0 --set rootno=0 --force --sql
5.7的ibdata1中能得到的元数据信息不多, 所以需要提供相关的表结构元数据信息, 然后使用–sdi指定即可.
恢复被truncate的表
mysql 5.7恢复被truncate的表
truncate相当于drop+create, drop的数据很快会被重写, 所以我们整个大点的表.
然后我们找下相关表的tableid值为62
再根据tableid去找indexid,值为62(这里和table_id巧合的重合了…)
然后我们扫描indexid=62的page
python3 main.py --scan /dev/vdb --set indexid=62
最后我们指定sdi/frm信息去解析对于的页即可.
python3 main.py ./ibd2sql_auto_dir_20260108_162317/index/0_0000000041_0000000000000062.page --sdi /data/mysql_5744/mysqldata/db1/t20260108_105.frm --set leafno=0 --set rootno=0 --force --sql --limit 1
419W的数据量,恢复了411W 还行.
8.0 恢复被truncate的表
8.0的就有难度了, 因为truncate的表是update的系统表, 看不到truncate之前的indexid了, 而且测试发现truncate前后表的indexid是变化的, 也就是无法根据indexid直接去找了.
但也不是完全没办法:
- 扫描所有的page,
- 排除当前已使用的indexid,
- 从剩下得到page里面一个个试.
先准备环境, 这次就200W数据吧.
扫描所有page
python3 main.py --scan /dev/vdc --set indexid=0,all
然后我们排除下已有的Indexid
可以简单使用shell筛选下:
ls ./ibd2sql_auto_dir_20260108_163711/index/ | awk -F '_' '{print $NF}' | awk -F '.' '{print $1}' | sort > /tmp/t20250108_01.txt
python3 main.py /data2/mysql.ibd --set table=indexes --sql --delete with | grep PRIMARY | awk -F ',' '{printf "%016d\n",$2}' | sort > /tmp/t20250108_02.txt
diff /tmp/t20250108_01.txt /tmp/t20250108_02.txt | grep '^<' | wc -l
还有85个, 就一个个试呗… 当然了, 还可以看下大小, 太小的也可以排除. 我这块盘就一个大表, 所以直接就定位到了. 然后解析下数据:
truncate之前是2097152条数据, 现在恢复的是2095322条数据, 就丢几千条, 还是能接受的.
总结
ibd2sql v2.2版本支持了 mysql 5.7和8.0的DROP TABLE和TRUNCATE TABLE的恢复, 但drop/truncate表的恢复技术只是保底的, 备份是很重要的.
本次只列出了一些用法, 更多用法可自行组合.
参考:
https://github.com/ddcw/ibd2sql/releases/tag/v2.2
软件下载地址:
https://github.com/ddcw/ibd2sql/archive/refs/tags/v2.2.tar.gz
转自
https://mp.weixin.qq.com/s/chO43BISJPqz9T9auLnCOA