抢救因误执行rm -rf 删除的MySQL数据

| No Comments

  在Linux服务器运维过程中,rm -rf * 命令因其不可逆的破坏性始终伴随着高危风险。不同于桌面环境的回收站机制,该指令一旦执行将直接导致物理存储层面的数据删除。根据文件系统原理,即便使用专业恢复工具,成功恢复的概率仍与磁盘写入状态密切相关,这使得误操作极易造成永久性数据丢失。

  下面是一次在Linux下对数据库执行rm -rf *后的恢复过程。涉及两个数据库共240张表,约5GB数据,数据库版本为MySQL 8.0.37。

过程:

一、文件恢复

  1、查看打开这些文件的进程ID:

root@mysql-server:~# lsof -L |grep /data/mysql/data
mysqld     272718                              mysql  cwd       DIR             252,17       20480   14155778 /data/mysql/data
mysqld     272718                              mysql   10uW     REG             252,17    12582912   14155781 /data/mysql/data/ibdata1 (deleted)
mysqld     272718                              mysql   12uW     REG             252,17    12582912   14155787 /data/mysql/data/ibtmp1 (deleted)
mysqld     272718                              mysql   14uW     REG             252,17    16777216   14155788 /data/mysql/data/undo_001 (deleted)
mysqld     272718                              mysql   15uW     REG             252,17    16777216   14155792 /data/mysql/data/undo_002 (deleted)
mysqld     272718                              mysql   23uW     REG             252,17  4315938816   14156109 /data/mysql/data/mydb/table_1.ibd (deleted)
mysqld     272718                              mysql   30uW     REG             252,17      114688   14155959 /data/mysql/data/mydb/table_2.ibd (deleted)
mysqld     272718                              mysql   31uW     REG             252,17    22020096   14156111 /data/mysql/data/mydb/table_3.ibd (deleted)
mysqld     272718                              mysql   32uW     REG             252,17    27262976   14156171 /data/mysql/data/mydb/table_4.ibd (deleted)

  2、查找进程文件描述符目录内容。

  上面的第二列 272718 就是MySQL服务的进程ID,接下来找到进程对应的文件操作描述符目录 /proc/272718/fd,看一下内容:

root@mysql-server:~# cd /proc/272718/fd
root@mysql-server:/proc/272718/fd# ll
dr-x------ 2 mysql mysql 256 Nov  1  2023 ./
dr-xr-xr-x 9 mysql mysql   0 Nov  1  2023 ../
lr-x------ 1 mysql mysql  64 Mar 12 17:35 0 -> /dev/null
l-wx------ 1 mysql mysql  64 Mar 12 17:35 1 -> '/var/log/mysql/error.log.1 (deleted)'
lrwx------ 1 mysql mysql  64 Mar 12 17:35 10 -> '/data/mysql/data/ibdata1 (deleted)'
lrwx------ 1 mysql mysql  64 Mar 12 17:35 106 -> '/data/mysql/data/mydb/table_1.ibd (deleted)'
lrwx------ 1 mysql mysql  64 Mar 12 17:35 118 -> '/data/mysql/data/mydb/table_3.ibd (deleted)'

  可以直接使用 cat 这些描述符来确定文件内容是否还在。

  3、使用IO重定向进行恢复。

  将文件恢复到本地,这里切勿和被恢复的文件放到相同的分区!!

# cat /proc/272718/fd/118 > /recovery/data/mysql/data/mydb/table_3.ibd

  或者将数据流传输至其他服务器进行保存。

# cat /proc/272718/fd/118 | ssh 192.41.6.5 'cat > /data/recovery/data/mysql/data/mydb/table_3.ibd'

  如果文件较多,可以结合awk sed或者记事本进行批量处理生成需要执行的重定向命令。

二、数据库恢复

  要将恢复回来的ibd文件导入到新库上,要求新库的数据库表结构必须与ibd内的数据使用的表结构相同,否则是无法使用导入ibd的方式恢复(后面会讲怎么处理)。

  1、创建并导入初始化用的数据库。

  2、导入ibd数据

-- 禁用外键约束检查
SET FOREIGN_KEY_CHECKS = 0;

-- 丢弃指定表的表空间
ALTER TABLE table_1 DISCARD TABLESPACE;
ALTER TABLE table_2 DISCARD TABLESPACE;
ALTER TABLE table_3 DISCARD TABLESPACE;

-- 将恢复回来的ibd文件拷贝到对应的数据库下面,然后进行导入,过程中不需要重启数据库服务

-- 导入表空间
ALTER TABLE table_1 IMPORT TABLESPACE;
ALTER TABLE table_2 IMPORT TABLESPACE;
ALTER TABLE table_3 IMPORT TABLESPACE;

-- 全部导入成功后重新启用外键约束检查
SET FOREIGN_KEY_CHECKS = 1;

  3、处理无法导入的ibd

  我遇到了4个表因为表结构问题致使无法导入ibd文件。接下来可以使用一个名为 ibd2sql 的开源工具,它可以从ibd文件中解析出sql语句。

  该脚本基于Python编写,可以从ibd文件中解析出sql语句,不需要安装第三方库,使用方法如下:

# wget https://github.com/ddcw/ibd2sql/archive/refs/tags/v1.9.tar.gz
# tar -zxvf ibd2sql-1.9.tar.gz
# cd ibd2sql-*
# cp /recovery/data/mysql/data/mydb/table_7.ibd .
# python3 main.py ./table_7.ibd --sql --ddl > table_7.sql

  接下来将生成的sql文件在目标数据库上执行,最后将整个恢复完的数据库导入到目标环境进行测试验证即可。

  脚本 Release Notes 里介绍的是解析 MySQL 5.7 的ibd文件,但是我用在MySQL 8.0.xx的ibd文件上也可以顺利解析,可能是由于这几个表数据不复杂吧。

最后:

  1、误删除文件切勿慌张,如果还存在访问该文件的进程,抓紧利用IO重定向的方法将数据备份出来;

  2、做好每日备份,如果是云主机最好也要开快照;

  3、执行rm操作时需格外谨慎,尽量避免使用rm -rf * 这样危险的命令;

  4、感谢 ibd2sql 工具作者。

  注:本次恢复成功存在较大偶然性,实际生产环境应建立多重数据保护机制,切勿依赖事后恢复。

本文结束。

Leave a comment