做数据库运维这些年,最怕听到的一句话就是:“我的数据库文件坏了,怎么办?”上周三晚上十一点,我正刷着手机准备睡觉,突然接到一个老客户的电话,声音都带着颤音。他们公司的ERP系统突然崩了,财务数据全打不开,库里的SQL数据库文件报错“系统表损坏”。我深吸一口气,远程连上服务器一看,好家伙,数据文件有80多个G,日志文件倒是一点没留。这种情况我见过太多次了——没人会提前做好备份,等出事了才想起来找救兵。

干这行的人都知道,SQL Server的数据库文件恢复,核心就两种场景:文件还在但打不开,或者文件直接被删了。前者多半是断电、磁盘坏道或者意外关闭导致的逻辑损坏,后者就是手滑或者恶意删除了。很多人一上来就想着用那些所谓的“一键修复工具”,结果越搞越糟。我见过一个最夸张的案例,有人用盗版工具扫描损坏的.mdf文件,工具直接往文件里写垃圾数据,本来还能读出50%的表,扫完后连表结构都认不出来了。所以第一步永远是:别慌,先评估损伤程度。
先说说最常见的情况——文件还在,但数据库状态显示“可疑”或者“恢复挂起”。这时候很多人会去网上搜一堆T-SQL命令,把数据库设为紧急模式,然后尝试重建日志。但这里有个坑:如果日志文件本身已经损坏,或者文件头被破坏了,强行设置紧急模式只会让SQL Server不断报错,甚至直接崩溃。我自己的经验是,先拿一个测试环境复制一份文件,在副本上操作。用DBCC CHECKTABLE命令逐表检查,记录下哪些表能正常读取,哪些表报错。这个过程很耗时间,但能帮你搞清楚到底损失了多少数据。
如果检查发现是页级别损坏,比如某个表的数据页有校验错误,那还有救。SQL Server自带的DBCC CHECKDB命令有个“REPAIRALLOWDATA_LOSS”选项,会直接删除损坏的页来恢复数据库可用性。听起来很粗暴,但实际用起来比你想的靠谱。我去年帮一个客户处理库存系统的数据库,有张订单表4个页坏了,用这个选项修复后,只丢失了大概200条订单记录,而整张表有80万条数据。不过要记住一点:这个操作会把损坏页里的数据全部丢掉,所以跑之前一定先做个完整备份。
真正头疼的是那种文件被误删的情况。很多人以为删了就没了,其实Windows系统在文件被删除后,只要没有写入新数据覆盖,文件内容还在磁盘上。这时候要立刻停止SQL Server服务,防止系统自动写入临时数据覆盖掉被删的文件。然后用专业的磁盘扫描工具去查找文件残留,像DiskGenius、R-Studio这些我都用过,能根据文件头签名扫描出.mdf文件的碎片。但这里有个技术细节:SQL Server的数据文件默认是8KB一个页,如果文件被删了之后磁盘上有碎片,扫描出来的碎片顺序可能是乱的,需要手动拼接。
讲个我自己的翻车经历。有次帮朋友公司恢复一个被离职员工恶意删除的数据库,文件大概30G,我花了两天两夜扫描磁盘,终于找回了80%的碎片。但拼接的时候发现有个关键的表——客户合同表——的碎片被新写入的日志文件覆盖了一部分。我只能用Hex编辑器打开文件碎片,根据表结构的元数据信息手动提取数据行。那真是体力活加技术活,一行一行地对偏移量,只恢复了合同表里70%的记录。从那以后,我再接恢复业务,第一件事就是问对方:有没有立刻停止服务器写入操作?
还有一种情况很多人不知道:数据库文件本身没坏,但日志文件太大了导致打不开。SQL Server的日志文件如果没做定期截断,能膨胀到几百个G。我见过一个客户,他们的财务系统日志文件有400多G,数据文件才20G。这种问题其实很简单,设置数据库为简单恢复模式,然后收缩日志文件就行。但很多人不敢这么操作,怕丢数据。其实简单恢复模式只是不保留事务日志备份,对数据文件的内容不会有影响。唯一要注意的是,收缩完日志后记得改回完整恢复模式,否则以后没法做时间点恢复。
想说一句:无论哪种恢复方法,永远别在原始文件上操作。我见过最离谱的案例,有人直接在出问题的服务器上跑各种修复工具,把原来的文件搞成碎片,连专业数据恢复公司都摇头。正确的做法是先停服务、备份原始文件、在测试环境里试方案。而且,别指望100%恢复——数据库损坏这种事,能救回来80%的数据就已经算成功了。如果你连备份都没有,那就当花钱买教训吧。毕竟,最好的恢复方案永远是“别让它坏”,而最好的预防手段就是:定时备份,多存几个副本。这话我每回跟客户说,他们都说“对对对”,但下次该不备份还是不备份。


