干这行十几年了,最怕半夜接到电话,那头传来急促的声音:“数据库崩了,日志报错,查不了数据。” SQL2000虽然老得像台古董机,但很多银行、医院、工厂的老系统还在用。一旦数据文件损坏,老板急得跳脚,业务停摆,那滋味真不好受。今天我就把压箱底的经验掏出来,聊聊怎么用最土的办法,对付最硬的骨头——SQL2000数据库修复。

先说个最经典的场景:数据库突然显示“可疑”状态,或者你运行DBCC CHECKDB时,报出一堆“表错误、索引损坏、分配错误”之类的鬼话。别慌,第一件事不是动手,而是备份。把mdf和ldf文件复制出来,存到安全的地方。这一步比什么都重要,因为修复操作本身就有风险,搞不好数据越修越少。备份之后,你再开始尝试“简单粗暴”的修复法:用单用户模式启动SQL Server,然后执行“ALTER DATABASE 库名 SET EMERGENCY”,让它进紧急模式。这时候SQL会尝试读取文件头,紧急模式允许你哪怕文件不完整也能访问。接着跑“DBCC CHECKDB (库名, REPAIRALLOWDATALOSS)”,这个命令会强制修复,但代价是丢掉损坏页面的数据。别怕丢数据,很多时候丢的只是索引碎片,真正的业务数据还在。
如果紧急模式加REPAIRALLOWDATALOSS还是搞不定,或者你不想丢数据,那就得用更“脏”的办法——直接操作数据页。SQL2000的数据文件是8KB一页的,每页有页头、数据行和行偏移数组。损坏通常发生在页头或行偏移区。你可以用十六进制编辑器(比如WinHex)打开mdf文件,找到报错的页号。比如DBCC报“页面 (1:123) 错误”,1是文件号,123是页号。计算偏移量:页号乘以8192(8KB),就是那页的起始位置。找到后,检查页头的前96字节,看页类型、LSN、对象ID是否正常。如果页头乱码,但数据行区域还能读出部分字符,你可以手动复制出可读数据,重新建个表插进去。这招很土,但对付文本型数据非常管用。我靠这招救过好几个工厂的库存表,虽然累得像狗,但客户感恩戴德。
还有一个常见坑:日志文件损坏导致数据库无法启动。SQL2000的日志文件是记录事务的,如果ldf文件坏了,数据库会认为事务不一致,拒绝启动。这时候别想着修日志,直接建立新日志。做法是:把mdf文件单独复制出来,ldf文件删掉或改名。然后启动SQL Server,执行“EXEC spattachsinglefiledb '库名', 'mdf文件路径'”。这个存储过程会强制附加单文件数据库,并自动生成新的日志文件。注意,因为缺少原日志,可能丢失未提交的事务,但至少数据库能上线。如果spattachsinglefiledb报错,就尝试“CREATE DATABASE 库名 ON (FILENAME='mdf路径') FOR ATTACHREBUILDLOG”,这个命令会重建日志。实操中,FOR ATTACHREBUILDLOG成功率更高,但要求mdf文件本身结构完整。
有些情况更棘手:数据库能启动,但某个表读不出来,一查询就报“I/O错误”或“磁盘满”。这时候可能是表所在的页面被标记为“可疑”,但物理上还能读。你可以用“DBCC PAGE”命令直接查看页面内容。先通过DBCC TRACEON(3604)打开输出,再执行“DBCC PAGE(库ID, 文件号, 页号, 3)”,第三个参数3代表显示详细数据行。如果能看到数据,就用BCP工具导出成文本文件。BCP命令像这样:“BCP 库名.dbo.表名 OUT 文件路径 -c -T”,-c代表字符格式,-T用信任连接。如果表名都查不了,就查系统表“sysindexes”找到该表的对象ID,再查“syscolumns”知道列名和类型,然后手动拼INSERT语句。虽然烦,但能救回关键数据。
说到系统表,SQL2000有个隐藏技能:直接用系统表恢复数据。比如“sysindexes”里存着每个表的索引信息,“syscolumns”存着列定义。如果表结构损坏导致无法访问,你可以从“sysobjects”里找到对象ID,然后从“sysindexes”里找到第一页的页号,再用DBCC PAGE直接读那页的数据。这招对“无法从sysindexes中找到行”的错误特别有效。我遇到过一家医院的挂号表坏得只剩几个页,就是靠这招把患者信息一条条抠出来的。注意,操作时一定要在单用户模式下,防止其他连接干扰。而且SQL2000的DBCC PAGE输出格式很乱,得耐心看十六进制和ASCII对照。
说个压箱底的东西:如果以上所有方法都失败,mdf文件头彻底烂了,连数据库都附加不上,那就只能用第三方工具了。比如DBS Tools for SQL Recovery,或者Stellar Phoenix SQL Database Repair。这些工具的原理是扫描整个mdf文件,识别出所有完整的数据页,然后重新组装成新数据库。价格不便宜,但比丢数据划算。不过别迷信工具,它们对简单表恢复率高,对复杂分区表、加密数据、大字段类型(text、image)效果差。我的经验是:先用工具跑一遍,能恢复多少算多少,然后结合手动抠页的方法补漏。记住,任何修复操作前,一定要备份原文件,否则一旦工具写坏文件,神仙也救不回来。
写这么多,其实就想说:SQL2000虽然老,但它不是铁板一块。数据损坏时,别急着格式化重装系统,也别直接扔给所谓的“专业修复公司”花冤枉钱。先冷静分析报错信息,判断是文件头、日志还是表页的问题,然后从最安全的紧急模式开始,一步步尝试。手动操作时,十六进制编辑器是你的手术刀,DBCC命令是你的听诊器。多练几次,你也能从一堆乱码里捞出客户的核心数据。毕竟,数据就是命。你救了数据,就等于救了客户的命。而这份手艺,永远是吃这行饭的硬通货。


