凌晨三点,值班手机像炸雷一样响起来。电话那头,运维同事的声音都在发抖:“库挂了,主从全断,业务全停了。”我套上外套往机房赶的时候,脑子里翻来覆去只有一个念头——数据库坏了,数据能不能救回来。

干这行十年,数据库故障见得多了,但每一次真出事儿,心跳还是得加速。MySQL、PostgreSQL、Oracle,不管哪个,数据恢复的黄金时间往往就那几分钟。你越慌,越容易犯低级错误,比如直接重启实例,结果把还在内存里没落盘的脏页给弄丢了。正确的第一步永远是:先冻结写入,再评估现场。
所谓“数据库坏了”,其实分好几种情况。最常见的是磁盘满了或者文件损坏,InnoDB的表空间文件读到一半报错;还有误操作,比如DROP TABLE、UPDATE忘加WHERE;再严重一点,是底层存储介质出了物理坏道。不同场景,恢复策略天差地别。你要是拿错方案,比如物理坏道还去跑CHECK TABLE,那等于在伤口上撒盐,数据只会碎得更彻底。
先说说逻辑损坏怎么处理。如果是误删数据,最优先的恢复手段是看备份。别笑,真有人因为备份策略形同虚设,只能去翻binlog。我见过一个案例,某电商平台凌晨两点有人误删了订单表,结果DBA翻出三小时前的全量备份,加上binlog增量,硬是花了四小时把数据拼了回来。要点是:备份文件的完整性要验证,别等到要用的时候才发现备份也是坏的。
如果没有备份,或者备份太旧,那就得靠binlog或者redo log来“续命”。比如MySQL,你可以通过mysqlbinlog工具把指定时间段的日志解析成SQL,再回放到一个新的实例上。这里有个坑:binlog格式得是ROW级别,如果是STATEMENT级别,遇到不确定函数(比如NOW())回放出来可能对不上。所以平时配置就要检查,别等出了事才后悔。
物理损坏就麻烦多了。InnoDB的ibd文件如果出现页损坏,启动时会报“Table doesn't exist”或者“Page corruption”。这时候别急着删表重建,先看看能不能用innodbforcerecovery参数把实例拉起来。这个参数从1到6,等级越高,跳过的检查越多,但数据丢失风险也越大。我的建议是:从1开始试,能起来就赶紧做逻辑备份(mysqldump),哪怕数据有缺失也比全没了强。
再狠一点的招儿,是用专门的数据恢复工具,比如Percona Data Recovery Tool for InnoDB,或者针对Oracle的ODU。这类工具直接扫描数据文件,跳过损坏的页,把能读出来的行记录抽出来。但操作门槛高,需要你对页结构、索引组织方式有了解。我见过一个老DBA,用ODU从一块物理坏道严重的盘里,硬是捞出了90%的业务数据,那场面跟考古现场似的。
文件系统层面也有可能出问题。比如ext4突然变成只读,或者目录项损坏导致文件“消失”。这时候先别慌,用fsck前一定要先做块级别的镜像。用dd或者ddrescue把整块盘克隆到另一块健康盘上,再在副本上操作。为什么?因为fsck本身就可能改坏数据,在原始盘上跑等于赌命。镜像完了,再挂载副本,用ext4magic之类的工具去恢复被删除的文件。
还有一个容易被忽略的点:数据库云服务商提供的快照恢复。如果你用的是RDS或者云上的自建实例,一般都有自动快照功能。但注意,快照恢复有时间点限制,可能丢最近几分钟的数据。这时候就得结合binlog或者WAL日志做时间点恢复(PITR)。操作流程是:先恢复到最近一个全量快照,再应用后续的日志归档,直到目标时间点。每一步都要记录日志的position,别跳步。
数据恢复成功的那一刻,心情确实爽,但千万别觉得完事儿了。后续的复盘比恢复本身更重要。要问三个问题:为什么故障会发生?为什么备份没兜住?为什么监控没提前报警?我见过太多团队,数据救回来了就庆祝,结果下个月同样的故障又来一遍。数据库坏了不可怕,可怕的是坏完了不长记性。
说个压箱底的经验:恢复出来的数据,一定要做抽样验证。别只看行数对得上就以为万事大吉,要随机抽查几条关键记录,对比业务侧的实际结果。因为损坏的数据有时候是静默的,行数没错,但某些字段可能已经被写坏了。验证通过后,再把数据导入新库,同时保留原始损坏文件至少一周,万一有问题还能再挖一次。
数据库故障这事儿,谁也躲不掉,但能不能把损失降到最低,拼的就是平时的准备和故障时的冷静。备份策略、日志保留、恢复演练,每样都得提前练熟。真到了数据库坏了那一刻,你靠的不是运气,是肌肉记忆。记住:恢复的本质是时间赛跑,但跑之前,先看清路。


