前段时间有朋友半夜给我打电话,声音都抖了:“我把生产库删了,怎么办?” 我问他有没有备份,他说有,但最近一次是三天前。我叹了口气,说你先别跑路,也别重启服务器,咱想想办法。
其实在 Linux 下,文件被删了不一定就彻底没了,尤其是在 ext3/ext4 这类日志文件系统上,只要你手快、步骤对,大概率能捞回一部分数据。今天就跟大家聊聊我平时处理这类“删库”事故的思路,顺便分享一个我一直在维护的小脚本,它集成了从确认文件状态到恢复数据的全流程。
先别急,我假设你现在就是那个不小心执行了 rm -rf /data/mysql/* 的倒霉蛋。第一步,立刻停止对这个分区的所有写操作。如果能卸挂,最好直接 umount,或者至少 mount -o remount,ro 挂成只读。这一步做不到的话,后面的恢复基本白搭。
接下来,你得找到被删文件在磁盘上的 inode 和位置。很多人知道 extundelete 这个工具,但它对 XFS、btrfs 这类现代文件系统无效。而且万一你的分区已经被部分覆写,恢复出来的文件可能损坏。所以这个脚本里我用的是 testdisk + ext4magic 的组合,外加针对 MySQL 的 ibd 文件恢复逻辑。
先从脚本的头开始看,它能自动帮你把分区挂成只读,然后扫描丢失的 inode:
#!/bin/bash
# recover.sh - 用于恢复误删的文件,重点适配 MySQL 数据目录MOUNT_POINT="/data"
DISK=$(df "$MOUNT_POINT" | tail -1 | awk '{print $1}')
BACKUP_DIR="/restore"# 1. 立即只读挂载,防止进一步覆写
echo "[*] 将 $MOUNT_POINT 重新挂载为只读..."
mount -o remount,ro "$MOUNT_POINT" || {echo "[-] 无法只读挂载,请手动 umount"exit 1
}
这里用 df 反向查出实际磁盘设备,省得你还要手动去查。如果自动挂载失败,脚本会提醒你手动 umount,毕竟在硬件 RAID 或者 LVM 下面,设备名可能不是你想的那样。
弄好只读之后,我会先用 ext4magic 去扫描被删文件。这工具比 extundelete 强在它可以按时间范围恢复,还能恢复目录结构。比如你昨天删的库,可以这么指定:
# 2. 使用 ext4magic 恢复最近24小时内被删的文件
echo "[*] 扫描 $DISK 上过去 1440 分钟内的删除记录..."
mkdir -p "$BACKUP_DIR/data"
ext4magic "$DISK" -a $(date -d '24 hours ago' +%s) -d "$BACKUP_DIR/data" -l
-a 后面跟的是 Unix 时间戳,-d 是指定输出目录,-l 会列出所有可恢复的文件,方便你确认到底删了啥。这步跑完,如果你运气好,能在 $BACKUP_DIR/data 下面看到一个和原来一模一样的目录树,里面就是恢复回来的文件。
但这是理想情况。很多时候你的文件系统已经被 MySQL 进程或者磁盘缓存搞乱了,.ibd 文件恢复出来可能不完整。这时候就得动用 MySQL 自带的 ibd_recover 工具(如果你用的是 InnoDB)。脚本里我加了一段专门处理这种烂文件的逻辑:
# 3. 如果恢复的 .ibd 文件损坏,尝试用 MySQL 工具重建
echo "[*] 检查并修复 .ibd 文件..."
find "$BACKUP_DIR/data" -name "*.ibd" | while read ibd; do# 检查文件是否完整(极简方式,看文件头魔法数字)if ! xxd -l 4 "$ibd" | grep -q "67de"; thenecho "[-] $ibd 文件头损坏,尝试修复..."# 使用 percona 的 ibdconnect 或自行通过表结构重建# 这里省略具体表的 ddl 提取,通常需要从其他备份拿建表语句mysql -e "CREATE TABLE ..."# 然后通过 import tablespace 强行导入,此处不展开fi
done
那一句 xxd -l 4 | grep "67de" 纯粹是经验之谈,InnoDB 的 .ibd 文件头两位一般是 0x67de,如果对不上,文件基本就残了。遇到这种情况,你只能找个旧的备份先把表结构建出来,然后把能读的页强行导进去。脚本里我不会帮你自动搞这步,因为风险太大,但至少给你留了个检查入口。
最后一步,如果你用的是 XFS 文件系统,ext4magic 就不管用了,得换 xfs_repair -n 检查,再用 xfs_undelete 这类工具。为了通用性,我加了文件系统类型判断:
# 4. 根据文件系统类型选择恢复策略
FSTYPE=$(df -T "$MOUNT_POINT" | tail -1 | awk '{print $2}')
echo "[*] 检测到文件系统类型: $FSTYPE"
case $FSTYPE inext3|ext4)# 已经在上面处理过了;;xfs)echo "[-] XFS 恢复需要 xfs_undelete 等工具,当前版本不支持自动恢复,请手动操作。";;*)echo "[-] 未知文件系统,脚本退出。"exit 1;;
esac
说白了,这个脚本就是个“删库急救包”,它并不能保证百分百恢复,但能让你在慌乱的时候按部就班走一遍,不至于越搞越糟。真正靠谱的方案永远是高可用+备份+延迟从库,别等到跑路才想起来。
整个脚本我一直在 GitHub 上维护,最近一次更新加了对 MySQL 8.0 新格式的兼容。如果你需要完整代码,可以去我置顶那篇文章里找下载链接(我会单独放出来,不然这篇文章没法过审)。希望你们永远用不上这个脚本,但万一用上了,记得先把分区只读,别手贱重启。
