作者:nzperfect / perfectaction
日期:2009.09.27
Email:
最近一直在進一步學習數據庫故障的處理方面的知識,做為一個數據庫維護人員,我即期望遇到所有的數據庫出錯的案例,以增加自己的經驗,但同時又擔心遇到這樣或那樣無法處理的數據庫故障而導致數據丟失。
前幾天看到一個文章,是說一個網站管理員在招聘DBA時,提出一個問題:“如果在SQL Server 日志裡發現一個頁損壞或是校驗和錯誤應該如何處理?”網站管理員描述,大概有90%的應聘者都會采用一個方案,用DBCC CHECKDB加上其中的一個修復選項,但其中也基本沒有人能具體解釋DBCC CHECKDB修復的過程或是工作原理及能修復到什麼程度。
借助聯機文檔以及個人的一些理解和經歷,解釋一下如何面對這個問題:"當數據庫出現頁損壞或校驗和出錯時如何處理?"
首先,需要先了解DBCC CHECKDB,聯機文檔url:
通過聯機文檔,可以得知有REPAIR_ALLOW_DATA_LOSS | REPAIR_FAST | REPAIR_REBUILD三個修復選項,而提供實際功能的只有REPAIR_ALLOW_DATA_LOSS和REPAIR_REBUILD兩個,其中REPAIR_ALLOW_DATA_LOSS 嘗試修復報告的所有錯誤,這些修復可能會導致一些數據丟失;而且REPAIR_REBUILD執行不會丟失數據的修復,包括快速修復(如修復非聚集索引中缺少的行)以及更耗時的修復(如重新生成索引);可見REPAIR_REBUILD是我們期望的。
當你從SQL Server log裡或是在程序查詢數據庫或是定期通過DBCC CHECKDB為數據庫做體檢的時候,出現了頁損壞或校驗和出錯信息時,如:
---------------------------------------------------------------------------------------------------------------------------------
M8928sg , Level 16, State 1, Line 1
Object ID 2088535921, index ID 0, partition ID 72345201021503994, alloc unit ID 72345201051571606 (type In-row data): Page (1:94299) could not be processed. See other errors for details.
Msg 8939, Level 16, State 98, Line 1
Table error: Object ID 2088535921, index ID 0, partition ID 72345201021503994, alloc unit ID 72345201051571606 (type In-row data), page (1:94299). Test (IS_OFF (BUF_IOERR, pBUF->bstat)) failed.
CHECKDB found 0 allocation errors and 2 consistency errors in table 'yourtable' (object ID 2088535921).
CHECKDB found 0 allocation errors and 2 consistency errors in database 'yourdb'.
repair_allow_data_loss is the minimum repair level for the errors found by DBCC CHECKDB (yourdb).
---------------------------------------------------------------------------------------------------------------------------------
現在我們應該如何做?
1.通過上面的提示,告訴我們:對象 2088535921出錯,它是一個表,頁面為1:94299
2.接下來,我們判斷損壞的頁在堆上還是聚集索引還是非聚集索引,SQL Server方法為:
dbcc traceon (3604, -1)
go
dbcc page('yourdb', 1, 94299, 3)
go
在輸出的結果裡(會報錯,但可以看到頁頭信息),可以看到
Metadata: IndexId = n
如果n是0而表示是堆,1表示是聚集索引,>1是表示非聚集索引
ps:其實從提示信息的Object ID 2088535921, index ID 0 ,也可以簡單判斷是堆.
3.根據上面的第2步,我們知道這個頁面是堆,這對我們來講,不是好消息,因為如果是>1,我們可以刪除該非聚集索引,再重建索引,不會丟失數據,而0或1則是元數據受損,這意味著有丟失元數據的可能性。
那麼如何僅僅修復這個數據頁呢,這裡我們假設該庫是full模式,並且有良好的備份策略,有全備和日志備份。
那麼我們可以進行頁面級還原操作,步驟如下:
a.首先進行一次日志備份,如果你不放心,還可以再做一個全備;
backup log yourdb to disk='D:\DBBak\yourdb_a.trn'
b.通過完整備份來恢復該page. (yourdb.bak是一個全備。);
restore database yourdb page= '1:94299' from disk='D:\DBBak\yourdb.bak' with norecovery
c.恢復這個全備之後的差異(假設有差異yourdb.dif),如果沒有差異備,直接到d步驟;
restore database yourdb from disk='d:\DBBak\yourdb.dif' with norecovery
d.恢復之後的log備份,可能有多個(假設為yourdb_1.trn,yourdb_2.trn);
restore log yourdb from disk='d:\DBBak\yourdb_1.trn' with norecovery
restore log yourdb from disk='d:\DBBak\yourdb_2.trn' with norecovery
restore log yourdb from disk='d:\DBBak\yourdb_a.trn' with norecovery
e.做一個最新的日志備;
backup log yourdb to disk='D:\DBBak\yourdb_e.trn'
f.還原最後的(e步驟)日志備份;
restore log yourdb from disk='d:\DBBak\yourdb_e.trn' with recovery
g.結束
4.經過步驟三之後,我們再來檢查一下該表是否還有錯,從提示信息Object ID 2088535921裡,我們查出表名tbname;
tbname: select object_name(2088535921)
然後 dbcc checktable('yourtable')檢測,如果沒有報錯,則表示修復完成
5.最後,對整個庫再做一次dbcc checkdb檢查;
ps:需要注意的是,SQL Server 的page級恢復在企業版和開發版中,支持聯機恢復page數據,在標准版只能脫機修復;
在dbcc checkdb修復選項裡,用repair_rebuild修復數據,聯機文檔稱是不丟失數據,但在某些環境下可能也會丟失數據,不過,我沒遇到過:)
用repair_allow_data_loss 選項時,聯機文檔稱可能會丟失數據,而對於堆或聚集索引的頁損壞,SQL Server 會釋放該頁面,造成數據的丟失,但repair_allow_data_loss選項有兩種情況是不會丟失數據,一種是非聚集索引上的頁錯誤,另外是 lob頁數據錯誤。
總紹:
一定要有良好的數據庫備份策略,備份重於一切;
要有異機備份,並且時時同步該備份文件;
當數據庫出現故障時,不要過於心急,冷靜分析一下錯誤;
如果不能確定如何做,可以借助google,如果你的錯誤信息裡中文的,請翻譯成英文後再google,這樣搜到解決方案的可能性更大;
做修復時,一定要再備一次數據庫;
dbcc checkdb的repair_allow_data_loss選項永遠是最後的選擇。
結束,如有錯誤,請指正