SQL Server 损坏修复 之三 不同部位损坏的应对

来源:互联网 发布:java replace多个字符 编辑:程序博客网 时间:2024/05/28 16:16

如果数据库或数据库备份受损,在检查数据库完整性的时候,会发现各种各样的错误。在这一节里,我们针对不同的损坏部位,给出不同的应对方法。首先,我们创建一个测试数据库。

 

CREATE DATABASE TESTDB

GO

USE TESTDB

GO

CREATE TABLE TESTTABLE

(ID int,

NAME nvarchar(50)

)

 

 --建立两个索引,其中一个是聚集索引,另外一个是非聚集索引

CREATE CLUSTERED INDEX idx1 on TESTTABLE (ID)

CREATE INDEX idx2 on TESTTABLE(NAME)

 

 -- 插入300行数据

DECLARE @i INT

SET @i = 1

WHILE (@i <= 300)

BEGIN

INSERT INTO TESTTABLE VALUES(@i, CONCAT('name_', @i))

SET @i = @i + 1

END

 

备份文件损坏

 

对前面建立的测试数据库做一个全备份,名为TESTDB.BAK。因为数据库比较小,所以备份文件也会比较小,用二进制文件编辑器如UltraEdit更改此文件一些地方的内容,以模拟该文件受损的情形,另存为TESTDB_BAD.BAK。执行如下命令,对数据库进行恢复:

RESTORE DATABASE TESTDB FROM DISK='D:\temp\TESTDB_BAD.bak'

 

在做恢复的时候,可能会碰到如下的错误,并且恢复过程会中断。

Processed 312 pages for database 'TESTDB', file 'TESTDB' on file 1.

Processed 3 pages for database 'TESTDB', file 'TESTDB_log' on file1.

Msg 3167, Level 16, State 1, Line 1

RESTORE could not start database 'TESTDB'.

Msg 3013, Level 16, State 1, Line 1

RESTORE DATABASE is terminating abnormally.

Msg 824, Level 24, State 2, Line 1

SQL Server detected a logical consistency-based I/O error: incorrectchecksum (expected: 0x9cb7ecfe; actual: 0x6f316d79). It occurred during a readof page (1:19) in database ID 8 at offset 0x00000000026000 in file 'C:\ProgramFiles\Microsoft SQL Server\MSSQL11.DENALI\MSSQL\DATA\TESTDB.mdf'.  Additional messages in the SQL Server errorlog or system event log may provide more detail. This is a severe errorcondition that threatens database integrity and must be corrected immediately.Complete a full database consistency check (DBCC CHECKDB). This error can becaused by many factors; for more information, see SQL Server Books Online.

 

这时,可以使用WITH CONTINUE_AFTER_ERROR参数尝试再次恢复:

RESTORE DATABASE TESTDB FROM DISK='D:\temp\TESTDB_BAD.bak'

 WITH CONTINUE_AFTER_ERROR

 

加CONTINUE_AFTER_ERROR, 虽然备份文件受损,但是会尽最大可能来继续恢复,显示恢复完成。

Processed 312 pages for database 'TESTDB', file 'TESTDB' on file 1.

Processed 3 pages for database 'TESTDB', file 'TESTDB_log' on file1.

Restore was successful but deferred transactions remain. Thesetransactions cannot be resolved because there are data that is unavailable.Either use RESTORE to make that data available or drop the filegroups if younever need this data again. Dropping the filegroup results in a defunctfilegroup.

RESTORE WITH CONTINUE_AFTER_ERROR was successful but some damage wasencountered. Inconsistencies in the database are possible.

RESTORE DATABASE successfully processed 315 pages in 0.271 seconds(9.055 MB/sec).

 

不幸的是,对数据库做完恢复后,该数据库有可能会马上处于Suspect(质疑)状态。我们还是无法使用该数据库。 我们把该数据库设置为紧急状态,并执行数据库修复命令:

ALTER DATABASE TESTDB SET EMERGENCY

DBCC CHECKDB(TESTDB)

DBCC CHECKDB(TESTDB, REPAIR_ALLOW_DATA_LOSS)

use TESTDB

SELECT * FROM TESTTABLE

 

上述命令都无法执行成功,并且会报如下错误:

Msg 945, Level 14, State 2, Line 4

Database 'TESTDB' cannot be opened due to inaccessible files orinsufficient memory or disk space.  Seethe SQL Server errorlog for details.

 

碰到这种情况,即使在恢复数据库的时候使用CONTINUE_AFTER_ERROR参数,但是还是无济于事。对于这个受损的备份文件,我们只能遗弃。

 

根据备份文件受损的地方不同,有时候还是能够使用上述方法找回一部分数据的。这很大部分依赖于在备份文件中受损的位置。所以CONTINUE_AFTER_ERROR这个参数,对于受损的数据库备份恢复,虽然是一个不错的尝试,但也并不是万能的。

 

日志文件损坏

下面是一个模拟日志文件损坏的测试。请运行如下的脚本操作。第一个UPDATE语句是更新ID=295的数据,并且马上做一个CHECKPOINT的动作,这会使得数据的更新,立刻写入数据文件中。第二个UPDATE语句打算要更新ID=296的数据,这两个操作是放在同一个事务内进行的。但是在第一个UPDATE语句结束后,不等WAITFOR语句结束,请马上杀掉数据库服务进程(Sqlservr.exe)。这样,在数据文件里,ID=295的记录已经被修改,ID=296的记录还没被修改。如果日志文件不损坏,下次SQLServer重新启动时,SQL会发现这个做到一半的事物,将ID=295的记录修改回滚。

BEGIN TRANSACTION

UPDATE TESTTABLE SET NAME = 'xxxx' where ID = 295

CHECKPOINT

WAITFOR DELAY '0:1:0'

UPDATE TESTTABLE SET NAME = 'xxxx' where ID = 296

COMMIT TRANSACTION

 

在这个测试里,我们编辑该数据库的日志文件,人为对数据库的日志文件造成损坏。当该数据库服务重新启动的时候,该数据库会处于RECOVERY_PENDING状态,查看数据库错误日志,有如下的错误,表明日志文件受损。

2012-04-15 22:49:57.930    spid26s       Error: 824, Severity: 24, State: 2.

2012-04-15 22:49:57.930    spid26s       SQL Server detected a logicalconsistency-based I/O error: incorrect checksum (expected: 0x6c1e7b9c; actual:0xd6a87d4a). It occurred during a read of page (2:0) in database ID 8 at offset0000000000000000 in file 'C:\Program Files\Microsoft SQL Server\MSSQL11

2012-04-15 22:49:57.930    spid26s       Error: 5105, Severity: 16, State: 1.

2012-04-15 22:49:57.930    spid26s       A file activation error occurred. Thephysical file name 'C:\Program Files\Microsoft SQLServer\MSSQL11.DENALI\MSSQL\DATA\TESTDB_log.ldf' may be incorrect. Diagnose andcorrect additional errors, and retry the operation.

2012-04-15 22:49:57.950    spid26s       File activation failure. The physicalfile name "C:\Program Files\Microsoft SQLServer\MSSQL11.DENALI\MSSQL\DATA\TESTDB_log.ldf" may be incorrect.

2012-04-15 22:49:57.950    spid26s       The log cannot be rebuilt because therewere open transactions/users when the database was shutdown, no checkpointoccurred to the database, or the database was read-only. This error could occurif the transaction log file was manually deleted or lost due to a

 

针对这种日志损坏,读者可以用下面的方法,重建数据库日志,以恢复数据库的正常访问:

ALTER DATABASE TESTDB SET EMERGENCY

ALTER DATABASE TESTDB Rebuild LOG on

(name=xxxx_log, filename='D:\temp\xxxx_log.LDF')

ALTER DATABASE TESTDB SET MULTI_USER

 

在重建数据库日志以后,一定要运行DBCC CHECKDB以确保没有一致性错误。

 

有一个要注意的地方,对数据库日志进行重建以后,我们会发现ID为295的NAME变成了xxxx,而ID为296的NAME依旧是name_296。如下图10-2所示。这从数据库的逻辑上来讲是没有问题的。但是从应用的角度,可能会大有问题。应用程序可能有逻辑需求,要求ID为295和ID为296的名字要么同时改变,要么同时不改变,在应用程序中,我们开启了事务以确保这一点。但是经过日志重建后,这点不能得到保证了。

10- 2 重建数据库日志后,保存下来的数据

因此,重建数据库的日志文件,是迫不得已的办法,会破坏数据的一致性。还是建议从好的数据库备份中恢复数据,这样能保证数据在业务逻辑上的一致。

 

用户数据文件损坏

让我们接着模拟数据库文件损坏的各种情形。新建测试数据库后,把TESTDB下线,编辑该数据库的数据文件,以模拟该数据文件受损的情形。然后把该数据库上线,运行DBCC CHECKDB命令,会报告发现数据库受损。根据受损的部位,有以下几个情形:

·        非聚集索引页面受损

具体的错误信息如下:

Msg 8939, Level 16, State 98, Line 1

Table error: Object ID 245575913, index ID 2, partition ID72057594039173120, alloc unit ID 72057594043564032 (type In-row data), page(1:228). Test (IS_OFF (BUF_IOERR, pBUF->bstat)) failed. Values are 133129and -4.

Msg 8928, Level 16, State 1, Line 1

Object ID 245575913, index ID 2, partition ID 72057594039173120,alloc unit ID 72057594043564032 (type In-row data): Page (1:228) could not beprocessed.  See other errors for details.

Msg 8980, Level 16, State 1, Line 1

Table error: Object ID 245575913, index ID 2, partition ID72057594039173120, alloc unit ID 72057594043564032 (type In-row data). Indexnode page (1:264), slot 0 refers to child page (1:228) and previous child(0:0), but they were not encountered.

Msg 8978, Level 16, State 1, Line 1

Table error: Object ID 245575913, index ID 2, partition ID72057594039173120, alloc unit ID 72057594043564032 (type In-row data). Page(1:265) is missing a reference from previous page (1:228). Possible chainlinkage problem.

DBCC results for 'TESTTABLE'.

There are 300 rows in 2 pages for object "TESTTABLE".

CHECKDB found 0 allocation errors and 4 consistency errors in table'TESTTABLE' (object ID 245575913).

 

读者会发现数据库受损是发生在表格TESTTABLE,而且是索引编号为2的存储上面。索引编号大于1的都是非聚集索引。因此,读者可以重建非聚集索引来尝试对数据库进行修复。

 

DROP INDEX idx2 ON TESTTABLE

GO

CREATE INDEX idx2 ON TESTTABLE(NAME)

 

再次运行DBCC CHECKDB(TESTDB)命令,检查结果显示,数据库中的数据是一致的。由于受损的地方恰好在非聚集索引页上,所以我们对数据库可以进行不丢失数据修复,如果受损在其他地方,如聚集索引页,则丢失数据难以避免。

·        聚集索引页面受损

 

根据第八章数据库空间管理中提到的方法,我们执行下面一系列查询,然后对聚集索引页面进行篡改:

 

SELECT * FROM sys.objects WHERE NAME = 'TESTTABLE'

-- 结果为245575913

 

SELECT * FROM sys.partitions WHERE object_id = '245575913'

-- 有两个索引,其中聚集索引所在地partition_id为72057594039107584

 

SELECT * FROM sys.system_internals_allocation_units WHERE

container_id = '72057594039107584'

-- 得知第一个数据页为(1, 226)

 

DBCC TRACEON(3604)

DBCC PAGE('TESTDB', 1, 226, 1)

DBCC TRACEOFF(3604)

-- 得知数据页的具体内容,编辑数据文件,找到相应位置,对该数据页面的内容进行篡改

 

得知数据页的具体内容后,把该数据库下线, 根据第一个数据页的查询得到的字符串,编辑数据文件,对该数据页内容进行篡改。然后上线该数据库,并对数据库做一致性检查,会发现如下错误:

Msg 8978, Level 16, State 1, Line 1

Table error: Object ID 245575913, index ID 1, partition ID72057594039107584, alloc unit ID 72057594043498496 (type In-row data). Page(1:231) is missing a reference from previous page (1:226). Possible chainlinkage problem.

Msg 8939, Level 16, State 98, Line 1

Table error: Object ID 245575913, index ID 1, partition ID72057594039107584, alloc unit ID 72057594043498496 (type In-row data), page(1:226). Test (IS_OFF (BUF_IOERR, pBUF->bstat)) failed. Values are 133129and -4.

Msg 8928, Level 16, State 1, Line 1

Object ID 245575913, index ID 1, partition ID 72057594039107584,alloc unit ID 72057594043498496 (type In-row data): Page (1:226) could not beprocessed.  See other errors for details.

Msg 8980, Level 16, State 1, Line 1

Table error: Object ID 245575913, index ID 1, partition ID72057594039107584, alloc unit ID 72057594043498496 (type In-row data). Indexnode page (1:230), slot 0 refers to child page (1:226) and previous child(0:0), but they were not encountered.

DBCC results for 'TESTTABLE'.

There are 63 rows in 1 pages for object "TESTTABLE".

CHECKDB found 0 allocation errors and 4 consistency errors in table'TESTTABLE' (object ID 245575913).

 

这次,数据页受损是在聚集索引页面上(索引编号为1),也就是在数据页面上,因此我们无法通过重建索引的方法对数据进行修复。我们采用REPAIR_ALLOW_DATA_LOSS的方法,对数据库进行修复。

 

ALTER DATABASE TESTDB set EMGERGENCY

ALTER DATABASE TESTDB set single_user withrollback immediate

GO

DBCC CHECKDB(TESTDB, 'REPAIR_ALLOW_DATA_LOSS')

GO

ALTER DATABASE TESTDB set multi_user

 

检索修复后的数据库,读者会发现,原来有300条记录的,现在只剩下了63条。有237条数据在修复的过程中丢失了。这是因为修复程序发现页内数据有问题,采用比较保守的办法,是把整个页面的数据删除,以维护数据的一致性。

 

·        系统页面受损

 

数据库有一些系统页面或系统表格非常重要,如在第八章数据库空间管理提及的PFS页面,GAM页面和SGAM页面。如果这些页面受损,那么整个数据库基本上很难修复。下面是一个示例。首先查询TESTDB的GAM页面信息,得到Slot 1里面的数据0000381f。

 

 

把数据库TESTDB下线,用二进制文件编辑器打开数据文件,对字符串0000381f进行篡改,然后把该数据库上线。数据库一上线后,TESTDB马上处于置疑(Suspect)状态,如图10-5所示。

 

我们把该数据库设为紧急模式,然后执行DBCC CHECKDB(TESTDB)

 

ALTER DATABASE TESTDB SET Emergency

DBCC CHECKDB(TESTDB)

 

会报如下的错误信息:

DBCC results for 'TESTDB'.

Msg 8998, Level 16, State 1, Line 1

Page errors on the GAM, SGAM, or PFS pages prevent allocationintegrity checks in database ID 5 pages from (1:0) to (1:517631). See othererrors for cause.

 

用下面的命令尝试对数据库进行修复:

ALTER DATABASE TESTDB SET Emergency

ALTER DATABASE TESTDB set single_user with rollback immediate

DBCC CHECKDB(TESTDB, 'REPAIR_ALLOW_DATA_LOSS')

 

由于系统页面损坏,所以即使用REPAIR_ALLOW_DATA_LOSS也无法对数据库进行修复。修复程序提示如下错误:

Msg 5028, Level 16, State 4, Line 3

The system could not activate enough of the database to rebuild thelog.

DBCC results for 'TESTDB'.

CHECKDB found 0 allocation errors and 0 consistency errors indatabase 'TESTDB'.

Msg 7909, Level 20, State 1, Line 3

The emergency-mode repair failed.You must restore from backup.

 

因此,如果系统页面或者系统表受损,则只能从备份当中恢复数据。

 

10.3.4系统数据库损坏

 

系统数据库包括master数据库,tempdb数据库,MSDB数据库和MODEL数据库。由于tempdb数据库会在数据库重新启动时,再次创建,因此对于tempdb数据库的损坏,我们可以重新启动数据库对其进行恢复。

 

master数据库,MSDB数据库和MODEL数据库一般情况下不会进行很多改动,所以如果碰到系统数据库损坏,从备份中恢复系统数据库是最好的办法。如果我们没有对系统数据库做备份,那么修复系统数据库的方法,则是从相同版本的数据库上,拷贝系统数据库的数据文件和日志文件,然后替换受损的系统数据库。具体方法,可参考前面章节对移动数据库的介绍。这样,损失的数据,包括master里面的数据和MSDB里面的数据。用户可以通过重建登录以及重建Job来进行恢复。

0 0
原创粉丝点击