在一個理想的世界中,不會存在任何數據庫的損壞,就像咱們不會將一些嚴重意外狀況列入咱們生活中的平常同樣,而一旦這類事情發生,必定會對咱們的生活形成很是顯著的影響,在SQL Server中也一樣如此,或許幾年內您沒有碰見過數據庫中出現這類狀況,而一旦碰見這類狀況,每每伴隨着數據的丟失,宕機,嚴重甚至您自己的職業生涯也會受到影響。所以對於這類狀況,咱們須要瞭解數據庫損壞方面的知識,以便咱們可以事前準備,過後可以處理。本篇文章會對數據庫損壞的緣由、現象、事前和過後的一些處理方法以及簡單的修復方法進行探討。數據庫
在瞭解數據庫損壞以前,首先咱們要了解SQL Server是如何將數據保存到數據文件(MDF、NDF等)。不管更新仍是插入數據,數據都須要首先在內存中的Buffer Pool駐留,而後經過CheckPoint和Lazy Writer等過程將內存中的數據持久化到磁盤。在這個過程當中,數據髒頁由內存寫入持久化的IO子系統,在此期間,按照IO子系統的不一樣,數據可能通過這幾層:服務器
所以,數據頁被寫入持久化存儲期間,可能通過上述列表中的幾項。在經歷上述過程當中,硬件環境會受到不少方面的影響,好比說電壓是否穩定、斷電、溫度太高或太低、潮溼程度等,而軟件方面,因爲軟件都是人寫的,所以就可能存在BUG,這些均可能致使數據頁在傳輸過程當中出現錯誤。多線程
此外,影響磁盤的因素也包括電壓是否穩定、灰塵等因素,這些也有可能引發磁盤壞道或總體損壞。app
上面提到的全部因素均可以被歸結爲IO子系統。所以,形成數據損壞的狀況絕大部分是由IO子系統引發的,還有很是很是小的機率內存芯片也會致使數據頁損壞,但這部分狀況微乎其微,所以不在本文的討論之列。編輯器
上面提到的這些致使數據損壞的緣由都屬於天災,還有一些人禍。好比說經過編輯器等手動編輯數據文件、數據庫中還有須要Redo和Undo的事務時(也就是沒有Clean Shutdown)刪除了日誌文件(一般會致使數據庫質疑)。大數據
在咱們知道可能形成數據庫的損壞緣由以後,接下來咱們來看SQL Server是如何監測數據庫頁損壞的。加密
在SQL Server的數據庫級別,能夠設置頁保護類型,一共有三個選項:None,CheckSum,Torn_Page_Detection,如圖1所示:spa
圖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和824錯誤都是錯誤等級爲24的嚴重錯誤,所以會被記錄在Windows應用程序日誌和SQL Server的錯誤日誌中,而引發該錯誤的頁會被記錄在msdb.dbo.suspect_pages中。SQL Server錯誤日誌中也會記錄到出錯頁的編號,如圖2所示。
圖2.824錯誤在SQL Server錯誤記錄中的描述
所以,若是咱們存在完善的備份的話,咱們能夠經過備份進行頁還原(在此再次強調一下對於DBA來講,有」備」無患),一個簡單的頁還原代碼如代碼清單1所示。
USE [master]
RESTORE DATABASE [Corrupt_DB] PAGE='1:155'
FROM DISK = N'C:\xxx.bak'
WITH FILE = 1, NORECOVERY, NOUNLOAD, STATS = 5
代碼清單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來驗證備份,來保證備份的數據沒有被損壞。
前面提到SQL Server發現錯誤的方法有兩種,分別爲在讀取頁時和在備份時(本質上也是讀取頁)。但若是咱們但願對於數據一致性的檢查更加的激進,那咱們應該按期使用CheckDB來檢查數據的一致性,而不至於在生產時間數據被讀取時才能發現錯誤。
CheckDB命令會對整個數據庫作全部的一致性檢查。當檢查對象是Master數據庫時,CheckDB還會檢查ResourceDB。
CheckDB最簡單的用法如代碼清單2所示,在當前數據庫上下文中直接執行CheckDB,將會檢查當前數據庫中全部的一切。
DBCC CHECKDB
代碼清單2.CheckDB最簡單的用法
CheckDB命令在企業版中會使用多線程來進行,會對整個數據庫進行一致性檢查,在該過程當中,使用了內建數據庫快照的方式進行,所以不會形成阻塞,但CheckDB會消耗大量的CPU、內存和IO。所以CheckDB要選擇在維護窗口時間或是系統閒時進行。
默認狀況下,CheckDB命令會將輸出全部的信息,但一般咱們並不關心這些信息,而是隻關心錯誤信息,所以實際中一般給DBCC指定不顯式信息的參數,如代碼清單3所示。
DBCC CHECKDB WITH NO_INFOMSGS;
代碼清單3.CheckDB一般搭配No_InfoMsgs參數
實際上,CheckDB是一套命令的彙總,CheckDB會依次檢查下述內容:
首先,當發現系統表損壞時,只能經過備份進行恢復(這也是爲何備份除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的原理及必要性和簡單的修復手段。對於數據庫損壞事前要作好充足的準備,在過後纔不會後悔莫及。就像買保險同樣,你可不會但願出了事之後再去買保險吧?