创建、分离、重新附加并修复一个置疑数据库

 

创建、分离、重新附加并修复一个置疑数据库


原文地址:

http://www.sqlskills.com/BLOGS/PAUL/post/TechEd-Demo-Creating-detaching-re-attaching-and-fixing-a-suspect-database.aspx

译者:

本文首先创建一个置疑数据库,然后作者用事实告诫世人:千万不要对一个置疑数据库分离再附加 。接着用“假载”( hack-attach )的方法将上面的数据库再次置疑,最后用将数据库置为紧急模式的方法来修复数据库。

译文:

自从多年前开博开始,我一直想讲述这个主题:如何重新附加一个已经分离了的置疑数据库。这是一个我常常在论坛上看到的情景:一个数据库置疑了,于是 DBA 试图分离后再附加,但是失败了。今年我为 TechEd 的课堂写了一个小 demo ,展示如何用十六进制编辑器创建一个置疑数据库。这将是一片很长的博文,但是我已经将明天去英国的行李整理好了,所以我还有点空闲时间。

创建一个置疑数据库

首先我将创建一个名为 DemoSuspent 的简单的数据库,里面创建一个表,并放一些数据。

USE MASTER
GO
CREATE DATABASE DemoSuspect
GO

USE DemoSuspect ;
GO

CREATE TABLE Employees ( FirstName VARCHAR ( 20 ), LastName VARCHAR ( 20 ), YearlyBonus INT );
GO
INSERT
INTO Employees VALUES ( 'Paul' , 'Randal' , 10000 );
INSERT INTO Employees VALUES ( 'Kimberly' , 'Tripp' , 10000 );
GO

现在我将在一个显式事务下进行一个更新,然后使用 CHECKPOINT 命令强迫数据写到硬盘上。我不小心将 Kimberly 的奖金删掉了。( Kimberly 好像是作者的妻子)。

-- Simulate an in-flight transaction
BEGIN TRAN ;
UPDATE Employees SET YearlyBonus = 0 WHERE LastName = 'Tripp' ;
GO

CHECKPOINT ;
GO

在另一个窗口上,我用下面的语句来模拟异常灾难:

SHUTDOWN WITH NOWAIT ;
GO

现在 SQL Server 关闭了,我将模拟对日志文件的造成一次 I/O 错误。我用一个十六进制编辑器来做——我选择的编辑器是非常流行且好用的、由 Christian Mass 开发的 XVI32 ,。我打开日志文件,将第一个部分用 0 填充,然后保存起来。截屏如下:

 

经过上面的修改,当我再次启动 SQL Server 时,它会对 DemoSuspect 数据库进行恢复,但是会失败。于是将数据库置为 SUSPENT 状态。

(译注:下面是具体的过程)

我重启 SQL Server ,试图进入 DemoSuspect 数据库。

USE DemoSuspect ;
GO

Msg 945, Level 14, State 2, Line 1
Database 'DemoSuspect' cannot be opened due to inaccessible files or insufficient memory or disk space. See the SQL Server errorlog for details.

现在来检查一下数据库的状态:

SELECT DATABASEPROPERTYEX ( 'DemoSuspect' , 'STATUS' ) AS 'Status' ;
GO

Status
--------
SUSPECT

此时,正确的步骤是恢复备份数据。如果没有备份,那么最好将数据库置为紧急( EMERGENCY )模式,然后尽可能多的从里面取出一些数据,或者运行紧急模式修复。

但是我准备试试分离在附加。

分离数据库

我用 sp_detach_db 来分离数据库。

EXEC sp_detach_db 'DemoSuspect' ;
GO

Msg 947, Level 16, State 1, Line 1
Error while closing database 'DemoSuspect'. Check for previous additional errors and retry the operation.

哈哈,起作用吗?

SELECT * FROM sys.databases WHERE NAME = 'DemoSuspect' ;
GO

没有答案,说明分离已经成功了。

重新附加数据库

我用 sp_attach_db 来附加数据库。

EXEC sp_attach_db @dbname = N 'DemoSuspect' ,  
   
@filename1 = N 'C:/Program Files/Microsoft SQL Server/MSSQL.1/MSSQL/Data/DemoSuspect.mdf' ,  
   @filename2
= N 'C:/Program Files/Microsoft SQL Server/MSSQL.1/MSSQL/Data/DemoSuspect_LOG.ldf' ;
GO

Msg 5172, Level 16, State 15, Line 1
The header for file 'C:/Program Files/Microsoft SQL Server/MSSQL.1/MSSQL/Data/DemoSuspect_LOG.ldf' is not a valid database file header. The PageAudit property is incorrect.

CREATE DATABASE…ATTACH_REBUILD_LOG 来附加如何?它应该为我建一个新的日志文件吧?:

CREATE DATABASE DemoSuspect ON
   ( NAME = DemoSuspect , FILENAME = N 'C:/Program Files/Microsoft SQL Server/MSSQL.1/MSSQL/Data/DemoSuspect.mdf' )
FOR ATTACH_REBUILD_LOG ;
GO

Msg 5172, Level 16, State 15, Line 1
The header for file 'C:/Program Files/Microsoft SQL Server/MSSQL.1/MSSQL/Data/DemoSuspect_LOG.ldf' is not a valid database file header. The PageAudit property is incorrect.
File activation failure. The physical file name "C:/Program Files/Microsoft SQL Server/MSSQL.1/MSSQL/Data/DemoSuspect_LOG.ldf" may be incorrect.
The log cannot be rebuilt because the database was not cleanly shut down.
Msg 1813, Level 16, State 2, Line 1
Could not open new database 'DemoSuspect'. CREATE DATABASE is aborted.

呵呵,数据库知道还有一个活动的事务。 ATTACH_REBUILD_LOG 命令只能在数据库清爽关闭且日志文件丢失时,才会建一个新的日志文件。如果我现在删除了日志文件并且试图用这个命令,我能欺骗它吗?我复制了数据文件和日志文件,然后删除远了的日志文件,试了试:

CREATE DATABASE DemoSuspect ON
   
( NAME = DemoSuspect , FILENAME = N 'C:/Program Files/Microsoft SQL Server/MSSQL.1/MSSQL/Data/DemoSuspect.mdf' )
FOR ATTACH_REBUILD_LOG ;
GO

File activation failure. The physical file name "C:/Program Files/Microsoft SQL Server/MSSQL.1/MSSQL/Data/DemoSuspect_LOG.ldf " may be incorrect.
The log cannot be rebuilt because the database was not cleanly shut down.
Msg 1813, Level 16, State 2, Line 1
Could not open new database 'DemoSuspect'. CREATE DATABASE is aborted.

没有成功。这个过程成功的希望本来不大,但是我们看到 SQL SERVER 更聪明了。

基本上,上面的问题是因为数据库没有被清爽地关闭,这样在附加时就不得不进行恢复进程。鉴于日志文件已经被损坏,所以恢复变成不可能完成的任务了。所以,永远不用分离一个置疑数据库。

现在要将数据库附加进 SQL SERVER 的唯一途径就是耍些小技巧:我将创建一个与已分离数据库有相同文件布局且大小尽可能接近的的“哑”数据库,然后我关闭 SQL SERVER ,将受损文件换进来,然后重启 SQL SERVER 。如果一切顺利的话,一个损坏了的置疑数据库又将附加成功了。(译注:为什么“又”?因为本文开始创建的就是一个受损的置疑数据库)

这个方法的最大的缺点是如果 SQL SERVER 实例使能即时文件初始化,而数据文件又太大,那么创建“哑”数据库将花很长时间。这意味着当文件创建时你的应用程序是脱机的。

(译注:下面是上面小技巧的实现。上面是理论,并没有实现。)

因为我在上面已经备份了受损的数据库文件了,现在需要删掉数据文件(译注:上面做分离 / 附加时已经删除了日志文件了)。在删除以前,你需要确保你已经拥有多个受损数据库文件的拷贝了,仅仅以防万一嘛。删除掉日志数据文件,我就可以创建我自己的“哑”数据库了。

CREATE DATABASE DemoSuspect
GO

如果你忘了先删除已存在的数据文件,那么你将会得到下面的错误:

Msg 1802, Level 16, State 4, Line 1
CREATE DATABASE failed. Some file names listed could not be created. Check related errors.
Msg 5170, Level 16, State 1, Line 1
Cannot create file 'C:/Program Files/Microsoft SQL Server/MSSQL.1/MSSQL/Data/DemoSuspect.mdf' because it already exists. Change the file path or the file name, and retry the operation.

OK ,删掉数据文件然后再重试。现在我需要检查一下我的“哑”数据库文件是否在那儿(我都有点成偏执狂了),关闭 SQL SERVER ,删掉“哑”数据库的文件,然后换进来受损的文件。我在换进来之前又再次了拷贝了受损文件,以防万一嘛。

重启 SQL SERVER 后,我可以检查数据库的状态了:

SELECT DATABASEPROPERTYEX ( 'DemoSuspect' , 'STATUS' ) AS 'Status' ;
GO

Status
--------
SUSPECT

哦,在经过关闭服务、又是删除文件又是拷贝文件之后,我又有了一个附加了的置疑数据库了。现在我可以真正地修复它了。

修复数据库

如果你没有任何备份,那么进入数据库的唯一途径就是使用紧急( EMERGENCY )模式。它虽然让你进入数据库但是你需要知道恢复过程并没有结束,因为数据库的内容是事务(可能结构也是)不一致的。我将选择在紧急模式下修改数据库,有关紧急模式下修改数据库的详情请见博文《CHECKDB From Every Angle: EMERGENCY mode repair - the very, very last resort 》。

ALTER DATABASE DemoSuspect SET EMERGENCY ;
GO
ALTER
DATABASE DemoSuspect SET SINGLE_USER ;
GO
DBCC
CHECKDB ( DemoSuspect , REPAIR_ALLOW_DATA_LOSS ) WITH NO_INFOMSGS , ALL_ERRORMSGS ;
GO

Msg 5172, Level 16, State 15, Line 1
The header for file 'C:/Program Files/Microsoft SQL Server/MSSQL.1/MSSQL/DATA/DemoSuspect_log.LDF' is not a valid database file header. The PageAudit property is incorrect.

File activation failure. The physical file name "C:/Program Files/Microsoft SQL Server/MSSQL.1/MSSQL/DATA/DemoSuspect_log.LDF" may be incorrect.
The log cannot be rebuilt because the database was not cleanly shut down.
The Service Broker in database "DemoSuspect" will be disabled because the Service Broker GUID in the database (9E879BFC-B742-4A69-AB14-4D6BD6F99E02) does not match the one in sys.databases (B4568D23-7018-40CF-B189-9C29DE697C09).
Warning: The log for database 'DemoSuspect' has been rebuilt. Transactional consistency has been lost. The RESTORE chain was broken, and the server no longer has context on the previous log files, so you will need to know what they were. You should run DBCC CHECKDB to validate physical consistency. The database has been put in dbo-only mode. When you are ready to make the database available for use, you will need to reset database options and delete any extra log files.

首先试着进行一次正规的 ATTRCH_REBUILD_LOG ,如果失败了, DBCC CHECKDB 便接管过来,它会试着用尽可能多的受损日志来进行恢复之后强制重建日志文件。然后它进行一次完整的修复,也许数据库里原来有一些问题,但是现在已经没有了,所以就没有错误信息输出了。

注意显示 Service Broker GUID 错误的那行。我是用了特殊的方法来重新附加数据库的,当我创建“哑”数据库时,系统会在 master.sys.databases 表中为 demoSuspect 数据库创建一个 Service Broker GUID 。当我换进来受损的数据库时,它的 GUID 和“哑”数据库的 GUID 是不同的——所以现在我不能使用 Service Broker 了。所有的一切都是因为对置疑数据库不是修复而是分离造成的。

那么现在数据是什么样子呢?

USE DemoSuspect ;
GO

SELECT * FROM Employees ;
GO

FirstName   LastName   YearlyBonus
----------  ---------  ------------
Paul        Randal     10000
Kimberly    Tripp      0

Kimberly 今年没有奖金了——她不会快乐的 。当然这有点假,但是这也显示出经过紧急模式修复后,日志受损时的活动事务并没有回滚。本文中,我是知道灾难发生时的情况的,但是如果有一个繁忙的有成百上千活动事务的 OLTP 系统将会怎样?最后的数据又将是什么呢?

总结

是的,你是可以将一个分离的置疑数据库恢复的,但是这一点都不漂亮而且你必须非常小心。最好的做法永远都是拥有一个完备的备份方案,这将使你能尽快的恢复数据库。如果你有一个置疑数据库并且又没有备份,那么使用紧急模式也可以存取或者修复数据库。

希望本文能对遇到同样困境的人有所帮助。

 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值