【SQLServer备份恢复】提高恢复速度:通过数据库完整备份,来恢复损坏的某个页面(一)

2014-11-24 10:16:21 · 作者: · 浏览: 0

如果数据库比较大,而只是某个数据页损坏了,那么并不需要恢复整个数据库,而只需要通过完整备份,来修复损坏的数据页就可以了,另外,应用后续的日志就可以完全恢复的这个数据页的数据。

测试环境为:SQL Server 2008R2

如果是其他的数据库版本,可能会导致下面的部分代码不能运行。

1、创建一个包含主文件组、读写文件组的测试数据库

use master
go

if DB_ID('db_test') is not null
   drop database db_test
go


CREATE DATABASE db_test
ON
(
	NAME = db_test_DATA,
	FILENAME = 'c:\db_test.mdf'
),

FILEGROUP FG_READ_WRITE
(
	NAME = db_test_RW,
	FILENAME = 'c:\db_test_RW.ndf'
)

LOG ON
(
	NAME = db_test_LOG,
	FILENAME = 'c:\db_test.ldf'
)
GO
2、创建表,插入数据
use db_test
go

create table xxx(v int,name varchar(100)) on FG_READ_WRITE

insert into xxx
select OBJECT_ID,name
from sys.objects
go

/*
运行下面的插入数据,由于数据量很大,导致长时间运行.
所以,点了"取消执行查询"按钮.

但通过查询,发现log_reuse_wait_desc的状态为 active transaction:

select name,log_reuse_wait_desc
from sys.databases
where name = 'db_test'

于是运行如下命令后,状态变为nothing,说明正常了:
dbcc checkdb(db_test)
*/
insert into xxx
select v,name
from xxx
go 10
3、完整备份
backup database db_test
to disk  ='c:\db_test.bak'
with format
/*
已为数据库 'db_test',文件 'db_test_DATA' (位于文件 1 上)处理了 184 页。
已为数据库 'db_test',文件 'db_test_RW' (位于文件 1 上)处理了 224 页。
已为数据库 'db_test',文件 'db_test_LOG' (位于文件 1 上)处理了 2 页。
BACKUP DATABASE 成功处理了 410 页,花费 0.525 秒(6.091 MB/秒)。
*/
4、再次插入数据
insert into xxx
select 0,'wc'
5、日志备份
backup log db_test
to disk = 'c:\db_test.trn'
6、通过未记载的命令:dbcc writepage来模拟损坏数据页的数据

请不要在生成环境中使用dbcc writepage,因为这个命令可能会导致数据的严重损坏。

  1. dbcc writepage的语法为:
  2. dbcc writepage
  3. ({ dbid,'dbname' }, fileid, pageid, offset, length, data)

    另外,通过如下的语句,可以查询出需要的修改的数据页,这个最后插入的一条v为0的数据,位于fileID=3,pageID=220,slot=215,这个%%lockres%%是sql server 2008才有的,可以用于返回数据所在的文件id,页id,记录id:

    select %%lockres%% as 'RID'
    from xxx
    where v = 0
    /*
    v	name	RID
    0	wc	3:220:215
    */

    生成100个字节的错误数据:
    select '0x'+REPLICATE('00',100)

    模拟损坏数据:

    dbcc writepage(db_test,3,220,20,100,
    0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
    )
    /*
    消息 8939,级别 16,状态 7,第 1 行
    表错误: 对象 ID 68,索引 ID 1,分区 ID 281474981167104,分配单元 ID 72057594037927936 (类型为 LOB data),页 (0:0)。测试(m_freeData >= PageHeaderOverhead () && m_freeData <= (UINT)PAGESIZE - m_slotCnt * sizeof (Slot))失败。值为 0 和 8192。
    DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
    */
    7、检查数据库,会显示页3:220 的错误:
    use master
    go
    
    dbcc checkdb(db_test)
    /*
    表错误: 对象 ID 0,索引 ID -1,分区 ID 0,分配单元 ID 72057594037927936 (类型为 Unknown),页 ID (3:220) 在其页头中包含错误的页 ID。页头中的 PageId = (0:0)。
    CHECKDB 发现有 0 个分配错误和 1 个一致性错误与任何单个的对象都没有关联。
    
    xxx的 DBCC 结果。
    消息 8928,级别 16,状态 1,第 1 行
    对象 ID 2105058535,索引 ID 0,分区 ID 72057594038779904,分配单元 ID 72057594039828480 (类型为 In-row data): 无法处理页 (3:220)。有关详细信息,请参阅其他错误消息。
    对象 'xxx' 的 204 页中有 55081 行。
    CHECKDB 在表 'xxx' (对象 ID 2105058535)中发现 0 个分配错误和 1 个一致性错误。
    CHECKDB 在数据库 'db_test' 中发现 0 个分配错误和 2 个一致性错误。
    对于由 DBCC CHECKDB (db_test)发现的错误,repair_allow_data_loss 是最低的修复级别。
    DBCC 执行完毕。如果 DBCC 输出了错误信息,请与系统管理员联系。
    
    */
    8、备份尾日志:
    backup log db_test
    to disk = 'c:\db_test_trail.trn'
    with no_truncate
    /*
    已为数据库 'db_test',文件 'db_test_LOG' (位于文件 1 上)处理了 6 页。
    BACKUP LOG 成功处理了 6 页,花费 0.100 秒(0.434 MB/秒)。
    */
    9、通过完整备份,修复数据页 3:220
    RESTORE DATABASE db_test
    		page = N'3:220'
    FROM DISK = 'c:\db_test.bak'
    with norecovery
    /*
    已为数据库 'db_test',文件 'db_test_RW' (位于文件 1 上)处理了 1 页。
    RESTORE DATAB