干这行这么多年,最怕听到的一句话就是:“库挂了,能恢复吗?”电话那头的声音往往带着点颤抖,背景里是同事们此起彼伏的讨论声。SQL Server恢复数据库这事,说简单也简单,一个RESTORE命令敲下去,等进度条跑完就完事。但说复杂也真复杂,遇到过太多案例,看起来是同一个报错,背后的原因却千差万别。你问我要个万能恢复方法,抱歉,真没有。但我能告诉你的是,绝大多数恢复失败,都栽在几个固定的坑里,而且这些坑,其实完全可以提前绕开。

第一个坑,也是出现频率最高的一个,就是备份文件本身不完整。我见过有同事把.bak文件从服务器拷到本地,中途网线松了,文件大小看着对,但内部结构已经损坏。还有更隐蔽的,有些第三方备份工具压缩出来的文件,SQL Server原生RESTORE命令根本不认。所以拿到备份文件,第一步别急着恢复,先检查文件大小和修改时间,最好用HEADERONLY选项看一眼备份头信息。这一串代码敲下去,如果返回了备份类型、数据库名称、备份日期这些信息,至少说明文件结构是完整的,可以继续往下走。如果报错说“备份集包含的文件数少于预期”,那基本可以断定文件有问题,别硬来。
第二个坑,是版本不兼容。SQL Server的备份文件不像文本文件那样随意,它跟版本绑定得很死。2008年的备份文件,你想恢复到2019年的实例上,没问题。但反过来,2019年做的备份,想恢复到2012年的库上,直接报错“数据库版本高于服务器版本”。这个坑踩的人特别多,尤其是公司做了版本升级之后,老备份文件全成了废纸。解决办法也简单,要么把老实例升级,要么用脚本把高版本库的结构和数据导出来,再导入低版本。但要注意,这种导出导入的方式会丢失一些高级特性,比如内存优化表、列存储索引,恢复完还得手动重建。
第三个坑,也是最容易让人崩溃的,就是日志链路断裂。很多新手只知道做完整备份,从来不做日志备份。等需要恢复到某个时间点时,发现日志链早就断了,只能恢复到最近一次完整备份的时刻,中间几个小时的数据全丢了。那感觉,就像你辛辛苦苦写了一下午文档,结果只保存了早上的版本。正确的做法是,完整备份加差异备份加日志备份,三个配合着来。恢复顺序也有讲究:先恢复完整备份,用NORECOVERY参数;再恢复最新的差异备份,还是NORECOVERY;依次恢复日志备份,一个用RECOVERY。这套流程走下来,才能恢复到故障前的一秒。
说到NORECOVERY和RECOVERY这两个参数,我得单独拿出来说两句。很多人搞不清楚它们的区别,或者压根没在意过。NORECOVERY的意思是“我还没恢复完,别让数据库上线”,这时候数据库处于Restoring状态,外部访问不了。RECOVERY则是“恢复完成,可以对外提供服务了”。问题就出在,有些人在恢复完整备份的时候直接用了RECOVERY,导致后续的差异和日志备份全都没法应用,数据库只能停在那个时间点。所以记住一个口诀:一个备份用RECOVERY,之前的全部用NORECOVERY。
还有个场景,很多人没意识到,那就是误删数据。比如昨天不小心DROP了一张表,今天想恢复。如果你有完整的备份策略,那就按我刚才说的步骤,恢复到昨天那个时间点。但问题是,恢复出来的库会把今天所有的新增数据也覆盖掉。这时候有个取巧的办法,就是恢复到另一个实例上,然后把那张被删的表导出来,再插回生产库。这个操作虽然麻烦,但比直接在生产库上做恢复要安全得多。我曾经帮一个客户这么操作过,他们业务24小时在线,不能停机,就是通过这种“旁路恢复”的方式,把损失降到了最小。
还有一类情况,是数据库文件本身损坏了,比如.mdf文件出现了物理坏页。这种情况下,常规的RESTORE DATABASE命令根本走不通,因为备份文件可能也是坏的。SQL Server提供了一些修复工具,比如DBCC CHECKDB,它能检测出哪些页坏了,但能不能修复得看运气。如果只是索引页损坏,运气好能修复;如果是数据页损坏,那就得考虑从备份还原了。所以平时一定要做定期的完整性检查,把DBCC CHECKDB加入维护计划,别等出事了才想起来。
我想说,恢复数据库这事,七分靠预防,三分靠技术。你技术再牛,遇到一个没有备份策略的库,也得干瞪眼。我见过太多公司,开发环境、测试环境、生产环境共用一套备份方案,结果生产库出问题时,开发库的备份文件根本不能用。备份策略一定要分级,生产库每天做完整备份,每小时做日志备份;测试库每周做一次完整备份就行。另外,备份文件一定要异地存储,最好存到对象存储上,别跟数据库放在同一台服务器,不然硬盘坏了,数据备份一起没,那才叫欲哭无泪。SQL Server恢复数据库,说到底不是一门手艺,而是一种习惯。养成好习惯,你永远用不上那些复杂的恢复技巧;但万一用得上,你至少不会慌。


