|
```html sqlserver数据库修复语句 上周三凌晨两点,据库朋友打电话过来,修复声音都在抖——他们公司那台跑了六年的语句SQL Server 2012突然报错,整个订单库变成"可疑"状态。据库我让他先别重启服务,修复直接连上去跑了一句: ALTER DATABASE 订单库 SET EMERGENCY; ALTER DATABASE 订单库 SET SINGLE_USER; DBCC CHECKDB (订单库,语句 REPAIR_ALLOW_DATA_LOSS); 三分钟后数据库能访问了,但代价是据库丢了七条下午刚写入的发货记录。他后来问我,修复有没有不丢数据的语句办法。有,据库但要看运气,修复也得看你对"修复"这两个字的语句理解。 SQL Server的据库修复语句其实就那几条,核心全在DBCC CHECKDB上。修复这东西平时没人碰,语句一出事就是救命稻草。但很多人不知道,REPAIR_ALLOW_DATA_LOSS和REPAIR_REBUILD的区别,不是"丢多丢少"的问题,而是"能不能接受逻辑不一致"的问题。前者会删掉损坏的页,后者只重建索引和非聚集结构,不碰数据页。所以如果你看到错误号是8909、8913这类,REPAIR_REBUILD基本没用,该丢还得丢。 我自己的习惯是分三步走。第一步,先备份当前损坏的数据库——哪怕它已经打不开,用BACKUP DATABASE 库名 TO DISK='...' WITH CONTINUE_AFTER_ERROR强制备份出来。这步很多人跳过,结果修到一半发现修错了,连原始状态都回不去。第二步,把数据库切到EMERGENCY模式,再切SINGLE_USER。为什么要EMERGENCY?因为普通模式根本不允许你跑修复语句,系统会直接拒绝连接。第三步才是CHECKDB加修复参数。 有个细节值得说:REPAIR_ALLOW_DATA_LOSS跑完之后,一定要再跑一次DBCC CHECKDB (库名) WITH NO_INFOMSGS,确认没有残留错误。我见过一次,第一次修复报成功,但第二次检查又冒出三个坏页。原因是修复过程中产生的临时页没被清理干净,得再修一遍。这种反复的情况在磁盘有坏道时特别常见。 另外,别迷信"自动修复"。SQL Server有个AUTO_CLOSE和AUTO_SHRINK,开着的时候容易在修复期间触发额外I/O,把问题搞复杂。修之前先关掉: ALTER DATABASE 订单库 SET AUTO_CLOSE OFF; ALTER DATABASE 订单库 SET AUTO_SHRINK OFF; 如果CHECKDB报的是分配错误而不是页错误,比如"IAM页链断裂",那还有另一种玩法:用DBCC CHECKALLOC先定位,再用DBCC CHECKTABLE (表名, REPAIR_ALLOW_DATA_LOSS)只修单表。这样影响面小,丢数据的风险也集中在一张表上。我去年帮一个客户修过一张2亿行的日志表,就是单表修复,最后只丢了三百多行,比全库修复强太多。 说句实在话,修复语句只是最后手段。真正靠谱的做法是平时开页校验(PAGE_VERIFY CHECKSUM),定期跑CHECKDB,别等数据库变可疑了才想起来。我那朋友后来装了监控,每周日凌晨自动跑一次完整性检查,再也没出过半夜惊魂的事。 还有一点,修复完别忘了DBCC CHECKDB之后要ALTER DATABASE 库名 SET MULTI_USER,不然应用连不上,又得打电话。这种低级错误我犯过,半夜脑子不清醒,修完直接关窗口,结果第二天早上被运维骂了一顿。 ``` |