SQL Server 資料庫損壞、檢測以及簡單的修復辦法

發表於2015-10-20

簡介

在一個理想的世界中,不會存在任何資料庫的損壞,就像我們不會將一些嚴重意外情況列入我們生活中的日常一樣,而一旦這類事情發生,一定會對我們的生活造成非常顯著的影響,在SQL Server中也同樣如此,或許幾年內您沒有遇見過資料庫中出現這類情況,而一旦遇見這類情況,往往伴隨著資料的丟失,當機,嚴重甚至您本身的職業生涯也會受到影響。因此對於這類情況,我們需要了解資料庫損壞方面的知識,以便我們能夠事前準備,事後能夠處理。本篇文章會對資料庫損壞的原因、現象、事前和事後的一些處理方法以及簡單的修復方法進行探討。

 

資料庫為什麼會損壞?

在瞭解資料庫損壞之前,首先我們要了解SQL Server是如何將資料儲存到資料檔案(MDF、NDF等)。無論更新還是插入資料,資料都需要首先在記憶體中的Buffer Pool駐留,然後通過CheckPoint和Lazy Writer等過程將記憶體中的資料持久化到磁碟。在這個過程中,資料髒頁由記憶體寫入持久化的IO子系統,在此期間,按照IO子系統的不同,資料可能經過這幾層:

•Windows(寫資料一定呼叫的是WINDOWS API)
•Windows底層的中間層(防毒軟體,磁碟加密系統)
•網路卡、路由器、交換機、光釺、網線等(如果IO子系統不是直連的話)
•SAN控制器(如果使用了SAN)
•RAID控制器(IO子系統做了RAID)
•磁碟或SSD等持久化儲存器

因此,資料頁被寫入持久化儲存期間,可能經過上述列表中的幾項。在經歷上述過程中,硬體環境會受到很多方面的影響,比如說電壓是否穩定、斷電、溫度過高或過低、潮溼程度等,而軟體方面,由於軟體都是人寫的,因此就可能存在BUG,這些都可能導致資料頁在傳輸過程中出現錯誤。

此外,影響磁碟的因素也包括電壓是否穩定、灰塵等因素,這些也有可能引起磁碟壞道或整體損壞。

上面提到的所有因素都可以被歸結為IO子系統。因此,造成資料損壞的情況絕大部分是由IO子系統引起的,還有非常非常小的概率記憶體晶片也會導致資料頁損壞,但這部分情況微乎其微,因此不在本文的討論之列。

上面提到的這些導致資料損壞的原因都屬於天災,還有一些人禍。比如說通過編輯器等手動編輯資料檔案、資料庫中還有需要Redo和Undo的事務時(也就是沒有Clean Shutdown)刪除了日誌檔案(通常會導致資料庫質疑)。

 

發現資料庫損壞

在我們知道可能造成資料庫的損壞原因之後,接下來我們來看SQL Server是如何監測資料庫頁損壞的。

在SQL Server的資料庫級別,可以設定頁保護型別,一共有三個選項:None,CheckSum,Torn_Page_Detection,如圖1所示:

圖1.頁保護的三種選項

關於這三種選項,首先,請無視None,請不要在任何場景下選擇該選項,該選項意味著SQL Server不對頁進行保護。

其次是TORN_PAGE_DETECTION,在SQL Server中,資料的最小單位是頁,每一頁是8K,但是對應磁碟上往往是16個512位元組的扇區,如果一個頁在寫入持久化儲存的過程中,只寫了一半的頁,這就是所謂的TORN_PAGE_DETECTION,SQL Server通過每個扇區提512位元組中前2位作為後設資料,總共16個扇區32位4位元組的後設資料(頁頭中標識為:m_tornBits),通過該後設資料來檢測是否存在部分寫的TORN_PAGE,但該型別的頁驗證無法檢測出頁中的寫入錯誤,因此在SQL Server 2005及以上版本,儘量選擇CheckSum。

在SQL Server 2005及以上版本,引入了CheckSum,CheckSum可以理解為校驗和,當資料頁被寫入持久化儲存時,會根據頁的值計算出一個4位元組的CheckSum存於頁頭(頁頭中標識同為:m_tornBits),和資料在同一頁中一起儲存在資料庫中。當資料從IO子系統被讀取到記憶體中時,SQL Server會根據頁內的值再次計算CheckSum,用該重新計算的CheckSum和頁頭中儲存的CheckSum進行比對,如果比對失敗,則SQL Server就會認為該頁被損壞。

由CheckSum的過程可以看出,只有在頁被寫入SQL Server的過程中才會計算CheckSum,因此如果僅僅改變資料庫選項的話,則頁頭中的該後設資料並不會隨之改變。

 

與IO相關的三種錯誤

通過上述CheckSum的原理可以看出,SQL Server可以檢測出頁損壞,此時,具體的表現形式可能為下述三種錯誤的一種:

•823錯誤,也就是所謂的硬IO錯誤,可以理解為SQL Server希望讀取頁,而Windows告訴SQL Server,無法讀取到該頁。
•824錯誤,也就是所謂的軟IO錯誤,可以理解為SQL Server已經讀取到該頁,但通過計算CheckSum等值發現不匹配,因此SQL Server認為該頁已經被損壞。
•825錯誤,也就是所謂Retry錯誤。

其中, 上述823和824錯誤都是錯誤等級為24的嚴重錯誤,因此會被記錄在Windows應用程式日誌和SQL Server的錯誤日誌中,而引起該錯誤的頁會被記錄在msdb.dbo.suspect_pages中。SQL Server錯誤日誌中也會記錄到出錯頁的編號,如圖2所示。

圖2.824錯誤在SQL Server錯誤記錄中的描述(檢視大圖

因此,如果我們存在完善的備份的話,我們可以通過備份進行頁還原(在此再次強調一下對於DBA來說,有”備”無患),一個簡單的頁還原始碼如程式碼清單1所示。

程式碼清單1.一個簡單的頁還原始碼,從備份中還原檔案ID1中的第155頁

記得我們前面說的,在讀取頁計算校驗和時出錯,這既可能是被寫入持久化儲存的頁本身出錯,也可能是在頁被讀取的過程中出錯,此時SQL Server會嘗試從IO子系統中再次讀取該頁,最多可能是4次嘗試,如果在4次嘗試過程中校驗和通過,則會是825錯誤,否則是824錯誤。這裡要注意,與823和824錯誤不同的是,825錯誤是一個等級僅為10的資訊。

因此,由於有固定的錯誤編號,因此可以在SQL Server Agent中對823和824設定警報。

 

備份CheckSum

上述頁CheckSum只有在頁被使用時才會被校驗頁的正確性。在備份資料庫時,可以指定CheckSum選項來使得備份讀取的頁也計算校驗和,從而保證了被備份的資料庫是沒有損壞的。在圖3的備份選項我們可以注意到這兩條:

圖3.CheckSum和Continue_After_Error選項

如果啟用了CheckSum,當備份過程中發現了頁校驗和錯誤時,就會終止備份,而啟用了Continue_After_Error選項的話,在檢測到校驗和錯誤時,仍然繼續從而使得備份成功。

備份如果啟用了CheckSum選項,除去檢測每一頁的校驗和之外,還會在備份完成後,對整個備份計算校驗和並儲存於備份頭中。

此外,對於備份,我們還可以通過Restore Verifyonly with CheckSum來驗證備份,來保證備份的資料沒有被損壞。

 

DBCC CheckDB

前面提到SQL Server發現錯誤的方法有兩種,分別為在讀取頁時和在備份時(本質上也是讀取頁)。但如果我們希望對於資料一致性的檢查更加的激進,那我們應該定期使用CheckDB來檢查資料的一致性,而不至於在生產時間資料被讀取時才能發現錯誤。

CheckDB命令會對整個資料庫做所有的一致性檢查。當檢查物件是Master資料庫時,CheckDB還會檢查ResourceDB。

CheckDB最簡單的用法如程式碼清單2所示,在當前資料庫上下文中直接執行CheckDB,將會檢查當前資料庫中所有的一切。

程式碼清單2.CheckDB最簡單的用法

CheckDB命令在企業版中會使用多執行緒來進行,會對整個資料庫進行一致性檢查,在該過程中,使用了內建資料庫快照的方式進行,因此不會造成阻塞,但CheckDB會消耗大量的CPU、記憶體和IO。因此CheckDB要選擇在維護視窗時間或是系統閒時進行。

預設情況下,CheckDB命令會將輸出所有的資訊,但通常我們並不關心這些資訊,而是隻關心錯誤資訊,因此實際中通常給DBCC指定不顯式資訊的引數,如程式碼清單3所示。

程式碼清單3.CheckDB通常搭配No_InfoMsgs引數

實際上,CheckDB是一套命令的彙總,CheckDB會依次檢查下述內容:

•初次檢查系統表
•分配單元檢查(DBCC CHECKALLOC)
•完整檢查系統表
•對所有表進行一致性邏輯檢查(DBCC CHECKTABLE)
•後設資料檢查(DBCC CHECKCATALOG)
•SSB檢查
•索引檢視、XML索引等檢查

首先,當發現系統表損壞時,只能通過備份進行恢復(這也是為什麼備份除TempDB之外的系統表非常重要)。其次,在一個大資料庫中,做一次CheckDB時間會非常長,維護視窗時間或系統閒時的時間可能無法Cover這段時間,那麼我們可以將CheckDB的任務分散到CHECKALLOC、DBCC CHECKTABLE、DBCC CHECKCATALOG這三個命令中。

更多關於CheckDB的詳細資訊,請參閱:http://technet.microsoft.com/en-us/library/ms176064.aspx。

 

資料庫損壞的修復

資料庫損壞最行之有效的辦法就是存在冗餘資料,使用冗餘資料進行恢復。所謂的冗餘資料包括熱備、冷備、和暖備。

使用映象或可用性組作為熱備,當檢測到錯誤時,可以自動進行頁修復(映象要求2008以上,可用性組是2012的功能)。映象當主體伺服器遭遇824錯誤時,會向映象伺服器傳送請求,將損壞的頁由映象複製到主體解決該問題。對於可用性組,如果資料頁是在主副本上發現的,則主副本將會向所有輔助副本傳送廣播,並由第一個響應的輔助副本的頁來修復頁錯誤,如果錯誤出現在只讀輔助副本,則會向主副本請求對應的頁來修復錯誤。在這裡有一點值得注意的是,無論是哪一種高可用性技術,都不會將頁錯誤散播到冗餘資料中,因為SQL Server中所有的高可用性技術都是基於日誌,而不是資料頁。

其次是使用暖備或冷備來還原頁,我已經在程式碼清單1中給出了詳細的程式碼,這裡就不細說了。

如果沒有合適的備份存在,如果損壞的資料頁是存在於非聚集索引上,那麼你很幸運,只需要將索引禁用後重建即可。

如果存在基準的完整備份,並且日誌鏈沒有斷裂(包括差異備份可以Cover日誌缺失的部分),則可以通過備份尾端日之後還原資料庫來進行修復。

最後,如果基礎工作做的並不好,您可能就需要通過損失資料的方式來換回資料庫的一致性,我們可以通過DBCC CheckDB命令的REPAIR_ALLOW_DATA_LOSS來修復資料庫。使用該方法可能導致資料損失,也可能不會導致資料損失,但大部分情況都會通過刪除資料來修復一致性。使用REPAIR_ALLOW_DATA_LOSS需要將資料庫設定為單使用者模式,這意味著當機時間。

無論是哪種情況修復資料庫,都要考慮是否滿足SLA,如果出現了問題之後,發現無論用哪種方式都無法滿足SLA的話,那隻能檢討之前的準備工作並祈禱你不會因此丟了工作。

 

小結

本篇文章闡述了資料庫損壞的概念、SQL Server檢測損壞的原理、CheckDB的原理及必要性和簡單的修復手段。對於資料庫損壞事前要做好充足的準備,在事後才不會後悔莫及。就像買保險一樣,你可不會希望出了事以後再去買保險吧?

相關文章