如何在SQL Server裏進行頁級別的恢復

在今天的文章裏我想談下每個DBA應該知道的一個重要話題:在SQL Server裏如何進行頁級別還原操作。假設在SQL Server裏你有一個損壞的頁,你要從最近的數據庫備份只還原有問題的頁,而不是還原整個數據庫。

一、 如何破壞一個頁

第一步我想向你展示下如何建立表(或索引)裏有個特定頁損壞的情景,首先構造一些數據。

USE master
GO

CREATE DATABASE PageLevelRestores
GO

USE PageLevelRestores
GO

-- Create a table where every record fits onto 1 page of 8kb
CREATE TABLE Test
(
    Filler CHAR(8000)
)
GO

-- Insert 4 records
INSERT INTO Test VALUES (REPLICATE('A', 8000))
INSERT INTO Test VALUES (REPLICATE('B', 8000))
INSERT INTO Test VALUES (REPLICATE('C', 8000))
INSERT INTO Test VALUES (REPLICATE('D', 8000))
GO

-- Retrieve the selected records
SELECT * FROM Test;

下一步進行全備,這個備份包含了屬於Test表的所有頁。這非常重要,因爲接下來我們會破壞這個表的一個特定頁。爲了找出屬於Test表的頁,我用DBCC IND命令來返回所有屬於這個表的頁。

-- Perform a full database backup
BACKUP DATABASE PageLevelRestores TO DISK = N'C:\Backups\PageLevelRestores.bak'
GO
-- Retrieve the first data page for the specified table (columns PageFID and PagePID)
DBCC IND(PageLevelRestores, Test, -1)
GO

要破壞一個特定的頁,可以使用未公開的DBCC WRITEPAGE命令。

ALTER DATABASE PageLevelRestores SET SINGLE_USER WITH ROLLBACK IMMEDIATE;
-- Let's corrupt page 90...
DBCC WRITEPAGE(PageLevelRestores, 1, 90, 0, 1, 0x41, 1)
DBCC WRITEPAGE(PageLevelRestores, 1, 90, 1, 1, 0x41, 1)
DBCC WRITEPAGE(PageLevelRestores, 1, 90, 2, 1, 0x41, 1)
GO
ALTER DATABASE PageLevelRestores SET MULTI_USER;

這裏我模擬了有個存儲錯誤,寫了一些垃圾到存儲的頁裏。現在當你從表再次讀取數據庫,SQL Server會返回你824 I/O錯誤,因爲對損壞頁的校驗失敗了。

-- Retrieve the selected records
SELECT * FROM Test;

一旦SQL Server在I/O訪問期間檢測到損壞的頁,會將其記錄在msdb.dbo.suspect_pages裏。

SELECT * FROM msdb.dbo.suspect_pages;

對msdb裏對特定表進行監控是個很好的想法,可以得到你的數據庫裏是否有損壞的頁。現在我們讓事情變得更糟糕,往表裏插入另外一條記錄。

-- Now we have additional transaction that we don't want to loose...
INSERT INTO Test VALUES (REPLICATE('E', 8000));

 

二、 如何還原損壞的頁

現在你想恢復這個數據庫到正確狀態且不丟失數據,你會怎麼做?

首先要進行所謂的尾日誌備份 Tail-Log Backup:備份自上次事務日誌備份後的已發生的事務。

-- Backup the transaction log
BACKUP LOG PageLevelRestores TO DISK = 'C:\Backups\PageLevelRestores_LOG1.bak' WITH INIT;

在這裏還沒有進行過事務日誌備份,因此我們的備份會包含自完整備份後,所有已執行的事務。現在可以在SQL Server裏初始頁級別還原操作。使用傳統的RESTORE DATABASE命令,但只要指定想要還原的頁,不用還原整個數據庫。對於大型數據庫,這會有很大的區別。

USE master
-- Restore full database backup
RESTORE DATABASE PageLevelRestores
    PAGE = '1:90'
    FROM DISK = 'C:\Backups\PageLevelRestores.bak'
    WITH NORECOVERY
GO

現在到了棘手的部分:在執行RESTORE DATABASE命令後,要再進行一次事務日誌備份,因爲接下來你要保證在這個可用頁進行的所有改變用作還原。沒有這個額外的日誌備份,SQL Server不能把你的頁重新上線。

-- Backup the tail of the log...
BACKUP LOG PageLevelRestores TO DISK = 'C:\Backups\PageLevelRestores_LOG_TAIL.bak' WITH INIT;

進行完這個額外日誌備份後,你可以按正確的順序恢復所有日誌備份,最後把數據庫上線。

-- Restore all available log backups in the correct order
RESTORE LOG PageLevelRestores FROM
    DISK = 'C:\Backups\PageLevelRestores_LOG1.bak'
    WITH NORECOVERY
GO
-- Finally restore the tail log backup
RESTORE LOG PageLevelRestores FROM
    DISK = 'C:\Backups\PageLevelRestores_LOG_TAIL.bak'
    WITH NORECOVERY
GO
-- Finally finish with the restore sequence
RESTORE DATABASE PageLevelRestores WITH RECOVERY
GO

現在當你再次查詢表時,你會看到SELECT語句成功執行沒有任何I/O錯誤,在這個表裏沒有丟失任何數據。

USE PageLevelRestores
GO
-- Retrieve the selected records
SELECT * FROM Test;

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章