在今天的文章裏我想談下每一個DBA應該知道的一個重要話題:在SQL Server裏如何進行頁級別還原操做。假設在SQL Server裏你有一個損壞的頁,你要從最近的數據庫備份只還原有問題的頁,而不是還原整個數據庫。數據庫
第一步我想向你展現下如何創建表(或索引)裏有個特定頁損壞的情景,這裏咱們會進行一些魔術,由於開箱即用(out-of-box)的SQL Server自己不會引入任何損壞的頁(若是有的話,恭喜你找到了一個BUG)。咱們從建立一個新的數據庫,往新建的表插入一些記錄開始。工具
1 USE master 2 GO 3 4 CREATE DATABASE PageLevelRestores 5 GO 6 7 USE PageLevelRestores 8 GO 9 10 -- Create a table where every record fits onto 1 page of 8kb 11 CREATE TABLE Test 12 ( 13 Filler CHAR(8000) 14 ) 15 GO 16 17 -- Insert 4 records 18 INSERT INTO Test VALUES (REPLICATE('A', 8000)) 19 INSERT INTO Test VALUES (REPLICATE('B', 8000)) 20 INSERT INTO Test VALUES (REPLICATE('C', 8000)) 21 INSERT INTO Test VALUES (REPLICATE('D', 8000)) 22 GO 23 24 -- Retrieve the selected records 25 SELECT * FROM Test 26 GO
下一步我進行完整數據庫備份。這就是說這個備份包含了屬於Test表的全部頁。這很是重要,由於接下來咱們會破壞這個表的一個特定頁。爲了找出屬於Test表的頁,我用DBCC IND命令來返回全部屬於這個表的頁。spa
1 -- Perform a full database backup 2 BACKUP DATABASE PageLevelRestores TO DISK = N'C:\Backups\PageLevelRestores.bak' 3 GO 4 5 -- Retrieve the first data page for the specified table (columns PageFID and PagePID) 6 DBCC IND(PageLevelRestores, Test, -1) 7 GO
爲了破壞一個特定的頁,我使用未公開的DBCC WRITEPAGE命令。是的,在SQL Server裏有個可用的DBCC WRITEPAGE命令,但請不要告訴任何人……rest
1 ALTER DATABASE PageLevelRestores SET SINGLE_USER WITH ROLLBACK IMMEDIATE 2 GO 3 4 -- Let's corrupt page 90... 5 DBCC WRITEPAGE(PageLevelRestores, 1, 90, 0, 1, 0x41, 1) 6 DBCC WRITEPAGE(PageLevelRestores, 1, 90, 1, 1, 0x41, 1) 7 DBCC WRITEPAGE(PageLevelRestores, 1, 90, 2, 1, 0x41, 1) 8 GO 9 10 ALTER DATABASE PageLevelRestores SET MULTI_USER 11 GO
爲了使用DBCC WRITEPAGE,問題數據庫必須設置如代碼所示的單用戶模式(Single-User mode)。這裏我模擬了有個存儲錯誤,寫了一些垃圾到存儲的頁裏(是的,這個在你工做中也會碰到!)。如今當你從表再次讀取數據庫,SQL Server會返回你一個824 I/O錯誤,由於對損壞頁的校驗失敗了。日誌
1 -- Retrieve the selected records 2 SELECT * FROM Test 3 GO
一旦SQL Server在I/O訪問期間檢測到一個損壞的頁,在msdb.dbo.suspect_pages裏,損壞的頁也會被記錄,以下圖所示。code
1 SELECT * FROM msdb.dbo.suspect_pages
對msdb裏對特定表進行監控是個很好的想法,能夠獲得你的數據庫裏是否有損壞的頁。如今咱們讓事情變得更糟糕,下列代碼會往表裏插入另一條記錄。orm
1 -- Now we have additional transaction that we don't want to loose... 2 INSERT INTO Test VALUES (REPLICATE('E', 8000)) 3 GO
如今你是DBA,你想恢復這個數據庫到正確狀態且不丟失數據(像咱們在最後一步插入的記錄)。你會怎麼作?首先你要進行所謂的尾日誌備份(Tail-Log Backup):你要備份自上次事務日誌備份後的已發生的事務。blog
1 -- Backup the transaction log 2 BACKUP LOG PageLevelRestores TO 3 DISK = 'C:\Backups\PageLevelRestores_LOG1.bak' 4 WITH INIT 5 GO
在這裏尚未進行過事務日誌備份,所以咱們的備份會包含自完整備份後,全部已執行的事務。如今咱們能夠在SQL Server裏初始頁級別還原操做。這裏你使用傳統的RESTORE DATABASE的T-SQL命令,但你只要指定想要還原的頁,不用還原整個數據庫,咱們只要還原有問題的頁。若是你在處理很大的數據庫,這會有很大的區別。索引
1 USE master 2 -- Restore full database backup 3 RESTORE DATABASE PageLevelRestores 4 PAGE = '1:90' 5 FROM DISK = 'C:\Backups\PageLevelRestores.bak' 6 WITH NORECOVERY 7 GO
如今到了棘手的部分:在RESTORE DATABASE的T-SQL命令後,你要進行另外一個事務日誌備份。這個另外的日誌備份是須要的,由於接下來你要保證在這個可用頁進行的全部改變用做還原。沒有這個額外的日誌備份,SQL Server不能把你的頁從新上線。事務
1 -- Backup the tail of the log... 2 BACKUP LOG PageLevelRestores TO 3 DISK = 'C:\Backups\PageLevelRestores_LOG_TAIL.bak' 4 WITH INIT 5 GO
進行完這個額外日誌備份後,最後你能夠在正確的順序裏恢復全部的日誌備份,最後你能夠把數據庫恢復上線。
1 -- Restore all available log backups in the correct order 2 RESTORE LOG PageLevelRestores FROM 3 DISK = 'C:\Backups\PageLevelRestores_LOG1.bak' 4 WITH NORECOVERY 5 GO 6 7 -- Finally restore the tail log backup 8 RESTORE LOG PageLevelRestores FROM 9 DISK = 'C:\Backups\PageLevelRestores_LOG_TAIL.bak' 10 WITH NORECOVERY 11 GO 12 13 -- Finally finish with the restore sequence 14 RESTORE DATABASE PageLevelRestores WITH RECOVERY 15 GO
如今當你再次查詢表時,你會看到SELECT語句成功執行沒有任何I/O錯誤,在這個表裏沒有丟失任何數據。仍是很簡單,是否是?
1 USE PageLevelRestores 2 GO 3 4 -- Retrieve the selected records 5 SELECT * FROM Test 6 GO
在SQL Server裏如何進行頁級別恢復操做是每一個DBA應該知道的。它是你工具箱裏最重要的工具之一——尤爲當你在處理很大的數據庫時。不用恢復整個數據庫,你只恢復有問題的頁,整個恢復過程會很是快。
最後給你一個問題,各位看官:你是否有過在SQL Server進行頁級別的恢復,若是是的話——對你來講它有多難/簡單?請暢所欲言!
感謝關注!