凌晨三点,电话响了。那头是个老朋友,声音有点抖:“数据库挂了,SQL 2005,公司系统全瘫了。”我一边穿裤子一边想,SQL 2005这玩意儿,微软早已停止支持快十年了,还在跑的系统,要么是老板舍不得升级,要么是业务逻辑太复杂动不了。到了现场一看,情况比预想的还糟——数据库文件挂载不上,错误提示是“823 I/O错误”,这是物理坏道的典型信号。

说实话,看到这个错误的第一反应,我心里咯噔了一下。SQL 2005的修复工具比后来的版本弱太多,没有2012之后的在线修复功能,也没有2016那种自动化坏页恢复。只能用手动的方式,一条命令地试。而且这数据库是2005企业版,64位,文件大小已经撑到1.2 TB,单是跑个 DBCC CHECKDB 就得三四个小时。客户说,这个库是他们公司的核心业务系统,每天都在跑ERP,如果恢复不了,整个公司下个月的订单都没法处理。
我让运维把数据库文件先复制到一台备用服务器上,避免在原机上反复读写加重坏道。然后打开 SQL Server Management Studio,用单用户模式启动实例。这一步很关键——SQL 2005 在多用户模式下,如果数据库处于可疑状态,系统会自动拒绝任何连接请求。单用户模式等于绕过了这个限制,让你能手动处理。输入命令:这条命令的意思是:把数据库设为单用户模式,同时强行回滚所有未完成的事务。如果不加 WITH ROLLBACK IMMEDIATE,系统会等挂起的事务自己结束,可能等上几个小时甚至卡死。
设为单用户后,我试着用 DBCC CHECKDB 检查数据库完整性。结果显示“表错误:对象ID 123456,索引ID 1,页(1:2345)上的槽点不一致”。翻译成人话就是:索引里的某条数据指针指向了不存在的记录。这种情况在 SQL 2005 里很常见,因为 2005 的索引结构是 B 树,一旦数据页损坏,索引里的指针就会乱飞。好消息是,如果只是索引损坏,数据本身可能还在;坏消息是,必须先把索引修好,才能把数据读出来。
接下来就是老炮儿们熟悉的操作:这个命令会尝试重建所有损坏的索引并进行一致性检查。注意,REPAIRREBUILD 只能修复非聚集索引和某些页级别的错误,它不会动数据页本身。如果数据页坏了,这个命令就无能为力了。跑完后,错误提示变成了“修复操作已成功完成,但某些页可能已丢失数据”。说明索引修好了,但数据页确实有坏道。
数据页损坏意味着那几页里的数据可能永远找不回来了。但别急——SQL 2005 有个冷门功能叫“紧急模式”。命令是:在该模式下,数据库会变成只读,但允许执行对,你没看错,这个命令的名字就叫“允许数据丢失”。它的原理是把损坏的页标记为已删除,让数据库可以正常挂载,只是这些页里的数据会丢失。
客户一听“数据丢失”,脸都白了。我问他们最近一次完整备份是什么时候?他们说上周五。现在是周三,差了五天。如果直接允许数据丢失,这五天里录入的订单、客户信息、财务数据都得人工补录,而且有些数据根本补不回来,比如附件、日志记录等。于是只能权衡:是用备份回滚到上周五,损失五天数据但保证完整;还是赌一把,用紧急模式修复,只损失坏页里的数据,风险在于坏页可能正好包含关键记录。
我建议先跑一次 DBCC PAGE,看看损坏的页里到底存了什么。DBCC PAGE 是 SQL Server 的“读页工具”,可以查看数据页的原始内容。命令格式:它会输出页的十六进制内容以及记录信息。结果显示,损坏的页里只有两条订单记录和一条客户备注,都是非核心表的数据。客户评估后说,这两条订单可以手动补录,备注也能让销售重新确认。于是我们决定赌一把。
先做一次完整备份——注意,要在紧急模式下用 ,避免影响以后备份链。然后执行该命令跑了将近两个小时,期间服务器 CPU 飙升到 90%,磁盘 I/O 接近饱和。提示“修复操作已成功完成”。随后把数据库设回多用户模式:数据库终于挂载上了。
但事情还没完。数据库能打开不等于数据就完整。接下来要做完整性验证:先再跑一次 DBCC CHECKDB,确保没有新错误;然后对比备份和修复后的数据量——用 统计每个大表,看看是否有明显的行数差异。我们发现有张日志表少了 3000 多条记录,估计就是坏页里的数据。客户确认这些日志可丢弃,不影响业务。随后重建所有索引,因为修复过程中索引结构可能被调整,重建索引能提升性能。最后再做一次全库备份,保存修复后的状态。
客户问以后怎么办。我说 SQL 2005 就别再用了,赶紧升级到 2019 或 2022。他们苦笑说,系统是 2005 时代的,业务逻辑复杂到没人敢动。这我理解,很多老系统都是这样,代码里全是存储过程、触发器和游标,升级等于重写。但数据安全不能靠情怀。我建议至少做三件事:1. 启用完整恢复模式,配合定期日志备份,这样最多只会丢失一小时的数据;2. 把数据库文件放在 RAID 10 上,磁盘故障时还能换盘;3. 每个月跑一次 DBCC CHECKDB,提前发现损坏。
看着重启后的 ERP 系统,客户松了口气,我也松了口气。SQL 2005 的修复说到底是跟时间赛跑——你永远不知道坏页里存着什么,也不知道备份能不能用。唯一确定的是,数据库越老,修复工具越弱,操作越依赖经验和胆量。这次算是运气好,坏页没伤到核心数据。如果坏的是系统表或主键索引,那就是另一回事了。数据灾难后重生,靠的不只是技术,还有一点运气。但运气这东西,用一次少一次。


