服务器硬盘开始报错时,你只有一次机会:用 ddrescue 抢救数据
运维最怕的场景之一,不是服务器宕机——宕机重启就好;而是硬盘开始出现坏道,SMART 报错,读写时不时卡住,dmesg 里刷出 I/O error。这时候最大的风险不是「盘坏了」,而是**在盘坏透之前,你用了错误的方法去折腾它,把本可以救回来的数据彻底弄丢**。比如有人一慌就 mount 上去 fsck,或者用普通的 cp -r 去拷,结果坏道区域反复重试,磁头反复摩擦同一片区域,越读越糟,最后整块盘彻底挂了。
正确姿势是:先做整盘镜像(block-level image),在镜像上再恢复数据。而做这个镜像的最佳工具,是 ddrescue(注意是 GNU ddrescue,命令名通常是 ddrescue,而不是老旧的 dd_rescue)。它专门为「从正在损坏的介质上尽可能多地抢救数据」而设计:它会先快速扫一遍能读的区域,跳过坏道;然后再回头对坏道区域反复、小心地重试;并且支持断点续传,中途中断了可以接着来。这篇文章讲清楚:ddrescue 的完整工作流、三种策略的选择、各参数的含义与取舍、以及镜像做完之后怎么恢复文件。适用于服务器单盘故障抢救场景。
第一步:动手前,先做这三件事
在跑任何命令之前,先冷静做三件事,顺序不能错。
1. 立刻停掉对该盘的一切写入。 如果坏盘还挂载着并且有服务在写它,先卸载或让服务停止。继续写只会让坏区扩散。如果这是系统盘没法卸,至少停掉数据库和应用。
2. 记录当前磁盘健康状态,留证据。 跑一次 SMART 自检,把结果存下来,后面判断盘有多坏、值不值得救全靠它:
smartctl -a /dev/sdb > /root/sdb-smart-before.txt
smartctl -t short /dev/sdb # 顺便跑一次短自检重点看这几个字段:Reallocated_Sector_Ct(重分配扇区数,已经坏了的)、Pending_Sector(待处理的不稳定扇区)、Current_Pending_Sector、UDMA_CRC_Error_Count。如果 Pending 数字在持续增长,说明盘还在恶化,动作要快。
3. 准备一块容量不小于坏盘的良盘作为目标。 镜像文件通常比源盘略大(因为有坏区的重试和文件系统开销),建议目标空间至少留出源盘容量的 1.1 倍。目标盘别用同一块物理盘,也别用同一块盘的另一个分区。
第二步:安装 ddrescue
注意包名陷阱:Debian/Ubuntu 里 apt install ddrescue 装的**就是 GNU ddrescue**(可执行文件是 ddrescue),而 gddrescue 在有些发行版里是别名。装完后用 ddrescue --version 确认,输出里应该有 "GNU ddrescue"。别错装了 Kurt Garloff 的 dd_rescue,那是另一个工具,参数不同。
apt-get update
apt-get install -y ddrescue
ddrescue --version第三步:核查盘符,别搞反了源和目标
ddrescue 最惨烈的事故就是**把源和目标写反**,那会直接覆盖掉你要救的数据。所以动手前一定用 lsblk 和 blkid 反复确认:
lsblk -o NAME,SIZE,MODEL,SERIAL,MOUNTPOINT
blkid /dev/sdb
df -h | grep sdb把 /dev/sdb 的序列号和型号记下来,和目标盘 /dev/sdc 对比清楚。再三确认后再往下走。
第四步:核心命令——分阶段抢救
ddrescue 的精髓在于「分阶段」。不要一上来就让它死磕坏道。标准流程是三遍:
第一遍:快速扫,跳过坏区,先把好数据抢下来
ddrescue -f -n /dev/sdb /mnt/backup/sdb.img /mnt/backup/sdb.log参数解释:
-f:force,允许写入已存在的输出文件/设备(不加的话看到目标已有内容会拒绝)。-n:no-split,不做「切割重试」。这一遍只读能读的,遇到坏区快速跳过,目标是**用最短时间把 90% 以上的好数据先拿到手**,避免磁头在坏区反复摩擦。/dev/sdb:源盘。/mnt/backup/sdb.img:镜像输出文件。/mnt/backup/sdb.log:**日志文件,是整个流程的命根子**。它记录了哪些区块救成功、哪些失败。有了它,ddrescue 就能断点续传、增量重试。
这一遍可能要跑很久(取决于盘容量和损坏程度),对一块 1TB 的盘可能几小时到十几小时。建议放进 screen 或 tmux 里跑,防止 SSH 断线。
第二遍:切割重试,专攻坏区
ddrescue -d -r3 /dev/sdb /mnt/backup/sdb.img /mnt/backup/sdb.log参数:
-d:direct,绕过内核缓存直接访问设备。对坏盘更有效,能读到缓存掩盖的坏区。-r3:retries,对坏区最多重试 3 轮,每轮会用「切割」(split)策略把大坏区拆成小份逐个啃,能榨出更多数据。数值越大越耗时,通常 3 够用。
注意这里**没加 -n**,因为这一遍就是要做切割重试。因为它只处理日志里标记为「未救回」的区域,所以很快,不会重复扫全盘。
第三遍(可选):反向读取,最后的倔强
ddrescue -d -R -r1 /dev/sdb /mnt/backup/sdb.img /mnt/backup/sdb.log-R 是 reverse,反向读取。磁盘某些坏区正向读不出来,反向读可能因为扇区物理布局不同而侥幸成功。这一步是「死马当活马医」,成功率不高但对顽固坏区值得一试。
读懂 ddrescue 的实时输出
运行中你会看到一行不断刷新的状态,例如:
rescued: 931.51 GB, errsize: 2048 kB, errors: 17
current rate: 41.9 MB/s关键是这两个数:rescued 是已成功救回的数据量,越接近盘容量越好;errsize 是**还没救回的坏区总大小**,这个数是你的最终目标——能把它压到多小,决定了你数据恢复的完整度。如果 errsize 一直是 0,恭喜,盘其实没坏透,整盘数据都拿到了。如果 errsize 停在某个值不再下降,说明那片区域是真的物理损坏了,只能接受。
第五步:在镜像上恢复数据,别碰原盘
镜像做完后,**所有后续操作都针对镜像文件(或它映射出的虚拟设备),绝不要再碰原盘**。原盘原样留着,直到你确认数据全部恢复。
如果你想用文件系统工具检查镜像,有两种方式。方式是把它当成一个文件系统直接挂载(需要 loop 设备):
losetup -fP /mnt/backup/sdb.img # -P 会自动扫描分区表
losetup -a # 查看挂到了哪个 loop 设备
mount -o ro /dev/loop0p1 /mnt/rescue务必用 -o ro 只读挂载,避免任何写操作破坏镜像。如果分区被识别为 /dev/loop0p1、/dev/loop0p2 多个,就分别只读挂载去找你要的文件。
如果镜像里的文件系统本身有损坏(坏区所在位置恰好是元数据),挂载可能失败。这时候不要在原盘上 fsck,而是对镜像的副本做 fsck:
cp /mnt/backup/sdb.img /mnt/backup/sdb-fsck.img
e2fsck -f /mnt/backup/sdb-fsck.img在副本上折腾,坏了可以再来一次。命令里的 -f 是强制检查。e2fsck 对 image 文件会提示你是否处理,确认后它会尝试修复。
几个真实的坑与经验
坑一:忘记保存 log,等于白干。 log 文件是断点续传和重试的基础。跑之前就想好路径,中途别删。如果不小心没保存,重跑会从头开始,对坏盘是二次伤害。
坑二:把 -n 和 -r 用混了。 第一遍要 -n(快抢),后续重试要 -rN(精啃),两个阶段别搞反。第一遍就 -r3 会让磁头在最开始就死磕坏区,浪费大量时间甚至加重损坏。
坑三:盘在重试过程中彻底死了。 这就是为什么第一遍「快抢」如此重要——先把大部分数据拿到手,后面即使盘挂了,你也有 90% 的数据。这也是 ddrescue 优于 dd(dd 遇到坏道默认行为糟糕)的根本原因。
坑四:直接对 SSD 用。 SSD 的坏块管理和机械盘不同,ddrescue 依然能用,但要注意 SSD 掉盘的成因往往是主控失效而非盘片坏道,可能整盘瞬间失联,抢救窗口极短。SSD 出问题时,能快就快。
坑五:忘记 chkdsk/fsck 顺序。 镜像挂载后如果文件缺失或乱码,先记下哪些文件受影响,再决定是否 fsck。fsck 有时会把损坏的目录项直接删掉来「修复」,等于二次丢失,所以能只读导出就先导出。
备份心智:抢救是最后手段,预防才是正道
啰嗦一句但很重要:ddrescue 这类工具的存在,恰恰说明**平时的备份体系有多重要**。如果你有定期的异地备份(哪怕是 rclone 同步到对象存储、或者 rsync --link-dest 的快照),遇到坏盘时你根本不需要冒风险去抢救,直接从备份恢复即可。ddrescue 应该是「备份也没了,只能硬啃」时的最后一道防线,而不是你的常规数据恢复方案。
给你一个可落地的配置建议:监控上 SMTP/SMART 告警(smartd 一发现 Pending Sector 增长就发邮件),备份上做到 3-2-1(3 份数据、2 种介质、1 份异地),然后用今天讲的 ddrescue 流程作为「万一」的预案写进运维手册。三层叠加,才算真的对数据负责。