SQL Server 損壞修復



一 常見錯誤解讀

SQL Server 對數據庫損壞的錯誤類型做了細化,在此對幾個典型的錯誤作一下介紹。

錯誤信息是:“在文件 '%ls'中、偏移量爲 %#016I64x 的位置執行 %S_MSG 期間,操作系統已經向 SQL Server 返回了錯誤 %ls。”

“The operating systemreturned error %ls to SQL Server during a %S_MSGat offset %#016I64x in file '%ls'.”

例如:

Msg 823, Level 24, State 3, Line 1

The operating system returned error 5(Access is denied.) to SQLServer during a write at offset 0x0000000000e000 in file 'FilePath\FileName'.

823錯誤代表SQLServer在向操作系統申請某個頁面讀寫的時候遇到Windows讀取或寫入請求失敗。Windows返回的錯誤代碼和相應的文本會插入消息中。對於讀取操作,SQL Server在報出823錯誤之前已經重試讀取請求4次。

從錯誤產生的機制可以看出,823錯誤是發出一個頁面讀寫請求時發生的,和讀寫的內容沒有關係。所以823錯誤和SQLServer本身無關。通常是物理文件損壞導致此錯誤,但也可能是設備驅動程序導致的。如果某個數據文件上反覆出現823錯誤,要不就是硬件設備出了問題,要不就是數據文件已經發生了非常嚴重的損壞。這個錯誤基本上意味着數據頁裏的有效數據已經丟失,一般DBCCCHECKDB很難修復。

錯誤信息是:“SQLServer檢測到基於一致性的邏輯I/O錯誤 %ls。在文件 '%ls' 中、偏移量爲 %#016I64x 的位置對數據庫 ID %d 中的頁 %S_PGID 執行 %S_MSG 期間,發生了該錯誤。”

“SQL Server detecteda logical consistency-based I/O error: %ls. It occurred during a %S_MSG of page %S_PGIDin database ID %d at offset %#016I64x in file '%ls'.”

例如:

SQL Server detected a logical consistency-based I/O error: tornpage (expected signature: 0x0; actual signature: 0x4e0372a8). It occurredduring a read of page (1:0) in database ID 13 at offset 0000000000000000 infile 'S:\Microsoft SQL Server\MSSQL.1\MSSQL\Data\www71_global_Data.mdf'.

此錯誤表明Windows報告已從磁盤成功讀取頁,但SQL Server檢測到頁中存在邏輯錯誤。那麼SQLServer會檢測到哪些“邏輯錯誤”呢?常見的錯誤類型有以下幾種。

Checksum

SQL Server可以在寫入每個頁面時,根據頁面裏的數據算出一個校驗值,一同存儲到頁面裏去。當下次讀取頁面的時候,再根據這次讀到的頁面數據,算出一個新的校驗值。如果寫入和讀出的數據一模一樣,那麼兩個校驗值一定是相等的。而如果兩個校驗值不相等,意味着上次SQLServer寫入的數據和這次讀出來的內容一定不同,現在讀出來的數據有問題。通過這種方法,SQL Server能夠發現數據頁面損壞。

TornPage

殘缺頁(Torn Page)保護其實是一種對電源故障導致的頁損壞進行檢測的方法。例如,意外電源故障可能導致一個頁面只有一部分被寫入了磁盤。使用殘缺頁保護時,頁的每個512B扇區末尾會放置一個2位簽名(在將原來的2位複製到頁頭之後)。每次進行寫操作時,這個簽名在二進制數01和10之間交替,這樣始終可以確定是否只有部分扇區寫到磁盤。如果稍後讀取頁時發現某個位的狀態不正確,則說明該頁沒有被正確寫入,因此檢測到問題頁面,稱爲殘缺頁。相對於Checksum,殘缺頁檢測使用的資源最少,但是它的算法太簡單,無法檢測到磁盤硬件故障導致的所有錯誤。

ShortTransfer

讀到的數據長度比預期的少。例如,一個讀取要求預期可以讀到8KB的數據,可是實際只返回了4 KB。這也意味着當前讀到的頁面有損壞。

Bad PageId

在讀到頁面後,SQL Server會比較頁面開頭存儲的頁面編號和自己請求的目標編號。如果發現自己想要讀取的頁面是第200頁,而讀到的內容裏顯示它是第100頁,SQL Server就會觸發824錯誤。這種錯誤經常是因爲I/O子系統沒有正確地處理SQL Server的請求,傳給SQL Server一個錯誤的頁面,甚至是一個空頁面。

RestorePending

在SQL Server 的企業版裏,用戶可以要求在做還原的時候跳過一些有損壞的頁面(continue after error)。這些跳過的頁面就被標識成“Restore Pending”。如果有用戶想去訪問它,也會遇到824錯誤。

StaleRead

一些硬件系統經常發生漏寫的現象(SQL Server要求將某個頁面寫入硬盤文件,I/O子系統報告寫入已經完成,可是SQLServer下次讀取的時候,讀到的還是寫入前的內容)。由於讀到的老版本頁面本身沒有什麼問題,Checksum和Tong Page算法都不能檢查到錯誤。對這一類問題,SQLServer也有對策。在打開SQL Server啓動參數開關-T818以後,SQL Server會在內存裏維護一個哈希表,記錄下自己所有做過寫入動作的頁面最新的LSN(Log Sequence Number)值。在下次讀出頁面的時候,會去比較這兩個值是否相等。由於LSN是個自動增長的唯一值,每個發生新修改的頁面,LSN的值會比原來的要大。所以如果讀到的LSN與內存中存放的不一致,就說明上次的寫入請求沒有真正完成。這時824錯誤也會被觸發。

從上面的介紹可以看出,824雖然是一個“邏輯錯誤”,是SQLServer主動發現的數據損壞,但是損壞的來源,大都不是SQL Server自己。這裏的錯誤,主要是由於預期的寫入沒有完全完成導致的。所以824錯誤的原因,基本上還是在I/O子系統。由於SQL Server的讀寫請求是發給Windows,再由Windows發給底層的磁盤系統的,所以問題有可能發生在Windows以下的每一層,例如磁盤驅動器存在故障、磁盤固件存在問題、設備驅動程序不正確等。可以負責任地說,SQLServer自己是不會導致824錯誤的。

由於824錯誤是發生在頁面這一級的邏輯錯誤,所以很多時候DBCC CHECKDB能夠修復。但是這種修復也僅僅是邏輯上的,頁面裏面存儲的數據在824錯誤發生之前就已經丟失,SQL Server無法將它們修復回來。所以824錯誤基本也意味着部分數據丟失。

錯誤信息是:“嘗試在數據庫%d 中提取邏輯頁 %S_PGID 失敗。該邏輯頁屬於分配單元%I64d,而非 %I64d。”

“Attempt to fetchlogical page %S_PGIDin database %d failed. It belongs to allocation unit %I64d not to %I64d.”

例如:

Attempt to fetch logical page (1:584) in database 2 failed. Itbelongs to allocation unit 445237904015360 not to 72057594060079104.

605也是一個非常有名的數據庫損壞錯誤。此錯誤通常表示指定數據庫中的頁或分配已損壞。SQLServer會在根據頁鏈接或使用索引分配映射(IAM)讀取屬於表的頁時,檢測到此損壞。分配給表的所有頁必須屬於與該表相關聯的分配單元之一。如果頁眉中包含的分配單元ID不匹配與表相關聯的分配單元ID,將引發此異常。錯誤消息中列出的第一個分配單元ID是頁眉中顯示的ID,而第二個分配單元值則是與表相關聯的ID。

嚴重級別爲21表示可能存在數據損壞。造成的原因包括損壞的頁鏈、損壞的IAM或該對象的sys.objects目錄視圖中存在無效條目。這些錯誤通常由硬件或磁盤設備驅動程序故障而引起。

嚴重級別爲12表示可能存在暫時性錯誤,即在緩存中出現錯誤,但不表示對磁盤上的數據造成破壞。暫時性的605錯誤可由以下條件引發:

  • 操作系統過早地通知SQL Server已完成某個I/O操作;儘管不存在實際的數據損壞,但顯示錯誤消息。
  • 運行帶有優化器提示NOLOCK的查詢,或將事務隔離級別設置爲READ UNCOMMITTED。當使用NOLOCK或READ UNCOMMITTED的查詢嘗試讀取被其他用戶移走或更改的數據時,將發生605錯誤。若要驗證是否爲暫時性的605錯誤,可以稍後重新運行該查詢。

通常,如果在數據訪問期間發生該錯誤,但後續的DBCCCHECKDB操作在沒有出錯的情況下完成,則605錯誤可能是暫時的。

由於605這個錯誤意味着一些頁面分配出了問題,所以也是一個非常嚴重的數據庫損壞。一般用DBCC CHECKDB也很難修復。

其他

在SQL Server內部,除了文件頁面分配和每個頁面內部格式以外,還有一些其他的約束規則。下面是一些常見的錯誤例子。

PFS頁面頭有損壞:

Msg 8946, Level 16, State 12, Line 1Table error: Allocation page(1:13280496) has

invalid PFS_PAGEpage header values.Type is 0. Check type, alloc unit ID and page

ID on the page.

系統表上的聚集索引頁面上有損壞:

Server: Msg 8966, Level 16, State 1, Line 1 Could not read andlatch page (1:18645)

with latch type SH. sysindexes failed.

Msg 7985, Level 16, State 2, Server SUNART, Line 1System tablepre-checks: Object

ID 4. Could not read and latch page (1:51) withlatch type SH. Checkstatement

terminated due to unrepairable error.

某個字段的值不符合字段數據類型定義:

Msg 2570, Level 16, State 3, Line 1Page (1:152), slot 0 in objectID 2073058421,

index ID 0, partition ID 72057594038321152, alloc unit ID72057594042318848 (type

"In-row data"). Column "c1" value is out ofrange for data type "datetime". Update

column to a legal value.

元數據有損壞:

Msg 3854, Level 16, State 1, Line 2Attribute (referenced_major_id=2089058478)of

row (class=0,object_id=2105058535,column_id=0,referenced_major_id=2089058478,referenced_

minor_id=0)in sys.sql_dependencies has amatching row (object_id=2089058478)in

sys.objects (type=SN) that is invalid.

遇到這些錯誤,管理員需要用DBCCCHECKDB命令來檢查和修復。有些錯誤是可以不丟數據就能修復的,有些是要丟數據才能修復物理層面錯誤的,有些是即使丟數據也沒辦法修復的。現在就來介紹CHECKDB的使用技巧。

SQL Server 損壞修復 之二 DBCC CHECKDB

DBCC CHECKDB指令可以完成兩項任務:(1)檢查數據庫裏有沒有損壞發生。(2)盡力修復數據庫損壞,使數據能夠重新被正常訪問。所以哪怕是一個正常運行的數據庫,也建議定期運行這句指令,以確保沒有損壞發生。對於已經發生訪問錯誤的數據庫,應該在第一時間運行這句指令,瞭解損壞的範圍和程度。

那麼DBCCCHECKDB究竟做了哪些檢查呢?

在做些什麼

DBCC CHECKDB通過依次執行下列操作檢查指定數據庫中所有對象的邏輯和物理完整性:

  1. 檢查一些關鍵的系統表。
  2. 對數據庫運行DBCC CHECKALLOC。
  3. 對數據庫中的每個表和視圖運行DBCC CHECKTABLE。
  4. 對數據庫運行DBCC CHECKCATALOG。
  5. 驗證數據庫中每個索引視圖的內容。
  6. 驗證數據庫中的Service Broker數據。

這意味着運行了DBCC CHECKDB,就不必再單獨運行DBCC CHECKALLOC、DBCCCHECKTABLE或DBCC CHECKCATALOG命令。也意味着單獨運行DBCCCHECKALLOC、DBCC CHECKTABLE和DBCCCHECKCATALOG命令,雖然不能完全完成DBCC CHECKDB的所有功能,但是至少完成了大部分功能。

檢查一些關鍵系統表

在檢查數據庫之前,SQLServer需要去了解這個數據庫裏到底存放了什麼樣的數據,也就是所謂數據庫的“元數據”(Metadata)。沒有這些信息,SQL Server無法知道自己將要去訪問什麼樣的表格,也沒辦法知道自己應該怎麼解釋將要讀到的記錄。

關鍵系統表有:

sysallocunits。

syshobts。

syshobtcolumnes。

sysrowsets。

sysrowsetcolumns。

對於普通用戶訪問,這些表都是不可見的。只有在DAC模式下的連接,才能看到它們。它們的結構對普通用戶也是透明的。

這裏的每一張系統表都有一個聚集索引。SQLServer會像做CHECKTABLE一樣,對這些系統表做一遍檢查,確保這些表格裏的每一頁面及頁面裏的每一條數據都可以正確地讀出來。如果中間發現問題,例如發現某個頁面不能被正常訪問,SQLServer就會報錯。例如:

Server: Msg 8966, Level 16, State 1, Line 1

Could not read and latch page (1:33245) with latch type SH.Sysobjects failed.

對於小的數據庫,這些存放元數據庫的系統表不會佔用太多的頁面。如果發生硬件問題,損壞的機率比較小。但是對一些有成千上萬對象的數據庫,這些原數據系統表本身就可能使用了很多頁面,發生損壞的機率也隨之增大。由於這些系統表是正確讀取一切數據的根本,所以如果任意一張系統表上發生了損壞,DBCCCHECKDB都會直接失敗,數據庫也無法做任何修復。此時恢復數據庫的唯一方法,只有恢復數據庫的備份。

對數據庫運行DBCC CHECKALLOC

DBCC CHECKALLOC將檢查數據庫中所有頁的分配。它還可驗證各種內部結構,這些結構可用於跟蹤這些頁,以及它們之間的關係。

DBCC CHECKALLOC將返回以下結果集(值可能有所不同,我們以AdventureWorks數據庫爲範例)。

DBCC results for 'AdventureWorks'.
***************************************************************
Table sys.sysrowsetcolumns                Object ID 4.
Index ID 1, partition ID 262144, alloc unit ID 262144 (type In-rowdata). FirstIAM (1:139). Root (1:66). Dpages 0.
Index ID 1, partition ID 262144, alloc unit ID 262144 (type In-rowdata). 13 pages used in 1 dedicated extents.
Total number of extents is 1.
***************************************************************
'...'
***************************************************************
Table Production.TransactionHistoryArchive                Object ID 158623608.
Index ID 1, partition ID 72057594047037440, alloc unit ID72057594053459968 (type In-row data).
 FirstIAM (1:804). Root (1:6568). Dpages0.
Index ID 1, partition ID 72057594047037440, alloc unit ID72057594053459968 (type In-row data). 
622 pages used in 79 dedicated extents.
Index ID 2, partition ID 72057594052804608, alloc unit ID72057594059227136 (type In-row data). 
FirstIAM (1:2897). Root (1:12608). Dpages122.
Index ID 2, partition ID 72057594052804608, alloc unit ID72057594059227136 (type In-row data). 
124 pages used in 17 dedicated extents.
Index ID 3, partition ID 72057594052870144, alloc unit ID72057594059292672 (type In-row data).
 FirstIAM (1:2899). Root (1:12832). Dpages166.
Index ID 3, partition ID 72057594052870144, alloc unit ID72057594059292672 (type In-row data). 
168 pages used in 22 dedicated extents.
Total number of extents is 118. 

--對於每一個表格對象,檢查它每個索引上的每個分區的數據分配情況

'...'
Processed 317 entries insystem catalog for database ID 5.
File 1. The number ofextents = 2898, used pages = 22552, and reserved pages = 23179.
           File 1 (number of mixed extents =104, mixed pages = 827).
    Object ID 4, index ID 1, partition ID262144, alloc unit ID 262144 (type In-row data), data extents 1,
 pages 13,mixed extent pages 9.
 
'...' 

--對於每個數據文件,檢查上面的數據頁面分配情況

The total number of extents= 2898, used pages = 22552, and reserved pages = 23179 in this database.
       (number of mixed extents = 104, mixedpages = 827) in this database.
CHECKALLOC found 0allocation errors and 0 consistency errors in database 'AdventureWorks'.
DBCC execution completed.If DBCC printed error messages, contact your system administrator. 

-- 對於整個數據庫的總結

對數據庫中的每個表和視圖運行DBCC CHECKTABLE

對於指定的表,DBCCCHECKTABLE將檢查以下內容:

  • 是否已正確鏈接索引、行內、LOB及行溢出數據頁。
  • 索引是否按照正確的順序排列。
  • 各指針是否一致。
  • 每頁上的數據是否合理(包括計算列)。
  • 頁面偏移量是否合理。
  • 基表的每一行是否在每個非聚集索引中具有匹配的行,以及非聚集索引的每一行是否在基表中具有匹配的行。
  • 已分區表或索引的每一行是否都位於正確的分區中。

DBCC CHECKTABLE返回以下正確結果集。

DBCC results for 'HumanResources.Employee'.

There are 288 rows in 13 pages for object 'Employee'.

DBCC execution completed. If DBCC printed error messages, contactyour system administrator.

對數據庫運行DBCC CHECKCATALOG

檢查指定數據庫系統表裏記錄的元數據邏輯一致性。可能的錯誤例如:

Attribute (object_id=130099504)of row (object_id=130099504,column_id=1) in

sys.columns does not have a matching row (object_id=130099504) in sys.objects.

--在sys.columns視圖裏,有一個列不屬於sys.objects裏的任何一個表格或視圖

Attribute (referenced_object_id=130099504,key_index_id=2) of row

(object_id=1447012236)in sys.foreign_keysdoes not have a matching row

(object_id=130099504,index_id=2) in sys.indexes.

--在外鍵視圖sys.foreign_keys裏的一個外鍵在sys.indexes裏找不到對應的索引

Attribute (parent_object_id=130099504) of row (object_id=2061966422) in sys.objects does not have a matchingrow (object_id=130099504) insys.objects.

--某個對象的父對象不存在。(例如有一個主鍵對象存在,但是主鍵所依附的表格不存在了。)

這些錯誤在正常的數據庫裏不應該出現,除非用戶自己去直接修改了系統表裏的數據。

驗證數據庫中每個索引視圖的內容

SQL Server支持在某些視圖上建立索引,以提高視圖的性能。在視圖中一些經過計算才能得到的字段值,SQLServer會將它們存儲在索引頁面裏。下次可以直接使用,而不需要再做計算。

DBCC CHECKDB會將視圖再計算一遍,驗證索引頁面裏存儲的字段值都是正確的,從而保證索引視圖的數據可靠性。但是如果視圖查詢的源數據量非常大,CHECKDB會使用tempdb裏的空間協助計算。這個過程會比較消耗資源和時間。如果想要跳過這一步,可以使用PHYSICAL_ONLY這個參數。

由於索引頁裏的數據都是根據源數據計算出來的,如果CHECKDB發現有任何問題,修復起來還是比較容易的。只要把現有的有錯誤的數據刪除,新計算一遍重新存儲起來就可以。所以索引視圖上的問題一般不會導致數據丟失。

對於XML字段上建立的索引,SQL Server在這一步也會做類似的驗證。

驗證數據庫中的Service Broker數據

如果數據庫使用了ServiceBroker的功能,SQL Server還會調用ServiceBroker的組件,檢查相關的系統對象(Service Broker的queue、pipeline等)是否正常。這部分和傳統的表格索引等沒直接的關係。

最後,DBCCCHECKDB會打印出一句總結的話:

CHECKDB found 0allocation errors and 0 consistency errors in database 'AdventureWorks'.

如果您的數據庫沒有說“0allocation errors”和“0 consistency errors”,而是有若干個錯誤,那就意味着數據庫有損壞了,趕緊修吧。

提供的修復方法

默認DBCCCHECKDB只會驗證數據庫是否完好,不會主動去做數據庫修復動作。要嘗試修復數據庫,需要將數據庫設成單用戶模式,才能使用以下三個修復選項之一。

· REPAIR_ALLOW_DATA_LOSS

嘗試修復報告的所有錯誤。這些修復可能會導致一些數據丟失。

· REPAIR_FAST

保留該語法只是爲了向後兼容。未執行任何修復操作,請不要使用。

· REPAIR_REBUILD

執行次要、快速修復(例如,修復非聚集索引中的額外鍵)及耗時修復(例如,重新生成索引)。執行這些修復時不會有丟失數據的危險。

如果已通過使用ALTERDATABASE語句將數據庫設置爲緊急模式,那麼假如指定了REPAIR_ALLOW_DATA_LOSS選項,則DBCC CHECKDB可以對數據庫執行某些特殊修復,恢復的數據庫在物理層面的一致性,使其能夠重新訪問。但是這種修復是以丟失數據爲前提的。所以應當是最後手段,並只有在無法從備份還原數據庫時才採用。

將數據庫設成緊急模式並以REPAIR_ALLOW_DATA_LOSS子句運行DBCC CHECKDB時,將執行以下操作

  • 將由於I/O或校驗錯誤而被標記爲不可訪問的頁重新標記爲可訪問,就如同這些錯誤沒有出現過一樣。這樣用戶將能夠訪問這些頁面,雖然頁面裏的內容肯定有問題。
  • 將嘗試使用常規的基於日誌的恢復技術恢復數據庫。
  • 如果由於事務日誌損壞而導致數據庫恢復失敗,則將重建事務日誌。但是重建事務日誌可能會導致數據庫裏的事務不一致。

如果DBCCCHECKDB命令成功,則說明數據庫在物理結構上是正確的,並且數據庫狀態將設置爲ONLINE,用戶可以正常訪問。但是,數據庫可能包含一種或多種事務不一致的情況。修復操作也不會考慮表本身或表之間可能存在的任何約束。如果指定的表與一個或多個約束有關,建議在修復操作後運行DBCCCHECKCONSTRAINTS。如果必須使用REPAIR,則運行不帶有修復選項的DBCCCHECKDB來查找要使用的修復級別。如果必須使用REPAIR_ALLOW_DATA_LOSS級別,則建議在運行帶有此選項的DBCC CHECKDB之前備份數據庫。因爲DBCCCHECKDB造成的修改,用戶可能會無法接受,而這個時候也沒辦法再用備份恢復的方法恢復數據庫了。

對於REPAIR_ALLOW_DATA_LOSS,這裏的data loss不僅僅是指一些數據記錄可能被刪除,還指有些錯誤的記錄會保留在數據庫裏,讓用戶繼續使用。所以其實這是一種比較危險的選擇,只能在萬不得已的時候選用。

如果一個數據庫使用REPAIR_ALLOW_DATA_LOSS級別都不能修復,管理員又能怎麼辦呢?選擇真的十分有限。以下是可用的幾種方法:

(1)按照預先的備份恢復策略,恢復數據庫備份。

這個可以說是最好的辦法,能夠將數據庫恢復到一個一致的時間點。強烈建議管理員使用這個選擇。

(2)如果損壞發生在某些用戶對象上(用戶表、視圖、存儲過程等),可以把它們DROP掉試試。

(3)將數據庫設成緊急只讀模式,用“SELECT ... INTO”或其他方式,將數據導入到一個新建的空數據庫裏。

設置緊急模式的指令是:

ALTER DATABASE <DB_Name>SET EMERGENCY

這種方法能夠從數據庫中將所有能讀出來的數據都讀出來,挽救儘可能多的數據。但是損壞嚴重程度不一樣,丟的數據多少也不一樣。這樣救回來的數據庫各個數據表的狀態將會不一致,一般在邏輯上會有很大的問題。

還是老生常談,恢復備份永遠是最好的選擇。

許多用戶在運行DBCCCHECKDB之前有很大的顧慮,擔心CHECKDB會造成數據庫阻塞,嚴重影響正常的應用運行。在前面的章節裏也介紹了,CHECKDB的過程其實是一個比較複雜的過程,SQL Server爲了驗證一個數據庫的正確性,要做的事情還是非常多的。運行它而造成一些性能影響,也是難免的。另一方面,許多數據庫損壞是無法修復,或者雖然可以在物理上修復,但是邏輯上的錯誤是無法挽回的。如果等到用戶訪問數據的時候才發現數據庫損壞,可能已經爲時已晚,損失巨大了。所以對每個數據庫定期做CHECKDB工作,必須是數據庫管理員的日常工作之一。那麼管理員怎樣在這兩者之間平衡,既能夠有一個比較合理的週期CHECKDB,又儘量不影響數據庫應用性能呢?

首先,SQL Server作爲一個能夠支持超大數據庫的數據庫管理系統,在DBCC CHECKDB上不斷地改進着。在現在的應用中,數百GB以至上TB的SQL Server數據庫比比皆是。不是說有用戶在使用數據庫,就不能運行CHECKDB的。SQL Server通過下面這些技術大大提高了CHECKDB的速度,降低了它對併發用戶的影響,防止了阻塞。

內部數據庫快照

在執行DBCCCHECKDB命令時,數據庫引擎創建一個數據庫快照,並將其置於在事務上一致的狀態。然後,DBCC命令對該快照運行檢查,而不是對數據庫本身做檢查。DBCC命令完成後,將刪除該快照。這樣,CHECKDB命令就不需要申請許多鎖,可以良好地防止在執行時出現阻塞和併發問題。所以從阻塞的角度講,CHECKDB完全可以在多用戶的模式下正常使用,不需要等到一個所有用戶都離線的時候再做。

並行檢查對象

默認情況下,DBCCCHECKTABLE對對象執行多個線程並行檢查,以加快檢查速度。並行度由查詢處理器自動確定。最大並行度的配置與配置並行查詢相同。若要限制DBCC檢查可使用的處理器的最大數目,請使用sp_configure改變maxdegree of parallelism選項。

並行度其實是一把雙刃劍。越多的處理器在同時進行數據庫檢查,CHECKDB能夠完成得越快。可是在這段時間裏,就會有越多的系統資源被使用在CHECKDB上。其他同時在運行的連接可能就拿不到足夠的資源,性能反而會受到更大的影響。所以管理員需要根據具體情況,在兩者之間做一個平衡。決定是儘快完成CHECKDB更重要呢,還是要兼顧其他連接的性能。通過使用跟蹤標誌2528,可以禁用並行檢查。

PHYSICAL_ONLY

這個選項可以以較小的開銷檢查數據庫的物理一致性,並且能檢測出會危及用戶數據安全的殘缺頁、校驗和錯誤及常見的硬件故障。但對於有些檢查會忽略,如FILESTREAM數據。因此,針對生產系統中頻繁使用的情況,建議使用PHYSICAL_ONLY選項。使用PHYSICAL_ONLY選項可以極大地縮短對大型數據庫運行DBCC CHECKDB的時間。

既然有了這些技術,那麼現在SQLServer運行一次CHECKDB大概要多久呢?很不幸,這真是一個非常難以回答的問題。運行的時間和下面這些因素有關係。

(1)數據庫自身大小。

(2)當前系統I/O子系統的讀寫能力與繁忙程度。

CHECKDB需要把數據庫從頭到尾通讀一遍。這本身就是一個巨大的I/O工作。I/O子系統能多快地完成SQL Server提出的讀寫請求,是影響到CHECKDB速度的最重要的因素。

(3)當前系統CPU負荷。

CHECKDB本身也是個驗證工作。讓計算機去驗證什麼事情,就意味着讓CPU去做計算。所以有時候CHECKDB也會導致系統CPU負載加重。

(4)當前數據庫的併發修改量。

雖然SQL Server使用快照技術大大提高了數據庫運行CHECKDB時候的併發度,但是如果數據庫上的修改非常頻繁,快照數據庫的維護本身會是一個很耗資源的任務,會反過來影響CHECKDB的速度。

(5)存放tempdb磁盤的速度。

當數據庫非常大的時候,SQLServer需要很多內存來存放一些中間結果。有時候僅放在內存裏是不合適也是不可能的,需要藉助tempdb裏的空間。那麼tempdb磁盤的讀寫速度對CHECKDB的速度也會有顯著影響。

(6)數據庫裏對象的類型。

不同類型的對象,需要驗證其正確性所花費的代價是不一樣的。比較費資源的對象有非聚集索引、計算列(computed column)、off-rowLOB values、Service Broker、XML索引、索引視圖(indexed view)等。如果這些對象在一個數據庫裏使用得比較多,那麼同樣的大小,這樣的數據庫CHECKDB會更耗時一些。

(7)CHECKDB使用的參數。

有些參數可以讓SQL Server少做一些檢查。例如WITH NOINDEX可以讓SQL Server不用去做費時費力的非聚集索引檢查。WITH PHYSICAL_ONLY可以讓SQLServer只做物理結構完整性檢查等。

(8)數據庫裏面的錯誤類型和錯誤的數目。

如果SQL Server在CHECKDB的過程中發現了錯誤,它就會用更加複雜的算法來衡量錯誤的性質、範圍和影響,以便將最準確有用的信息返回。這會使得CHECKDB的時間大大延長。

根據2012年時的經驗,一個大於1 TB的數據庫如果沒有錯誤,CHECKDB在有些機器上用8小時就能夠跑完。而一個有成百上千錯誤的數據庫,哪怕只有兩三百GB,也有可能一天都跑不完。這個區別是很顯著的。

雖然比較難以估計做一個DBCCCHECKDB需要多長時間,但是讀者可以通過下面的語句,查詢做DBCC CHECKDB進度如何。有時候進度可能不太精確,但是我們至少心裏有數大概什麼時候能夠做完。

SELECT session_id,request_id, percent_complete, estimated_completion_time ,

DATEADD(ms,estimated_completion_time,GETDATE())AS EstimatedEndTime,

start_time, status,command

FROM sys.dm_exec_requests

WHERE command like '%DBCC%'

超大數據庫上的最佳實踐

談了半天,結論還是兩個:對超大數據庫,CHECKDB本身是一個比較昂貴的任務,可能會影響並行運行的其他連接的性能;而不運行CHECKDB又是很危險的,如果長期不運行,突然發現已經積累了很多錯誤,這種風險幾乎無法承擔。那麼怎麼安排好這個任務呢?

如果數據庫裏設計了分區表(partitionedtable)機制,做起來可能比較簡單一些。對於存儲歷史數據的分區文件組,由於數據本身已經不會發生修改,我們可以把文件組類型設成只讀模式,防止任何誤修改。每個月或者每兩個星期對它們運行一次DBCCCHECKFILEGROUP即可。對於當前的數據,由於隨時都可能發生讀寫,發生損壞的機率也高得多。所以可以每個星期,甚至一個星期兩次,單獨做DBCCCHECKFILEGROUP。

如果數據庫沒有分區機制,CHECKDB就是一個宏大的工程了。幾乎可以說,沒有機會讓管理員找到一個時間窗口能把一個完整的CHECKDB做完。這時候怎麼做呢?

有一種折中的方法,是把數據庫裏面的所有表格按照它們佔用的頁面數量大致分爲7組,每組的頁面數目大致一樣。然後按照這樣的方式把CHECKDB裏面的關鍵任務分散在每天運行。

週一到週三:

每天運行一組DBCCCHECKTABLE。

週四:

DBCC CHECKALLOC + 一組DBCCCHECKTABLE。

週五週六:

每天運行一組DBCCCHECKTABLE。

週日:

DBCC CHECKALLOC + DBCC CHECKCATALOG + 一組DBCC CHECKTABLE。

用這種方法,可以在代價比較小的前提下完成CHECKDB的大部分工作。TB級數據庫的管理員可以考慮試試。

如果您有一臺備用服務器,則可以考慮不在生產環境上運行DBCCCHECKDB。而是把生產環境的數據庫通過備份方式,還原到備用服務器上,在備用服務器運行DBCCCHECKDB。如果備用服務器上檢查結果數據是完整的,這說明在對生產環境數據庫做備份的那一刻,數據是完整的。如果備用服務器上執行的結果顯示數據完整性有問題,則一定要在生產環境再次運行DBCCCHECKDB,以明確問題到底出在哪個環節。是生產環境的數據已經有問題,還是還原到備用服務器時出問題。這時候檢查數據完整性的優先級高於性能影響。所以在生產環境再次運行DBCCCHECKDB是必須的。使用這種方法的缺點是,需要額外一臺服務器,以及比較完善的自動化執行方案。

三 不同部位損壞的應對

如果數據庫或數據庫備份受損,在檢查數據庫完整性的時候,會發現各種各樣的錯誤。在這一節裏,我們針對不同的損壞部位,給出不同的應對方法。首先,我們創建一個測試數據庫。

CREATE DATABASE TESTDB
GO
USE TESTDB
GO
CREATE TABLE TESTTABLE
(ID int,
NAME nvarchar(50)
) 

--建立兩個索引,其中一個是聚集索引,另外一個是非聚集索引

CREATE CLUSTERED INDEX idx1 on TESTTABLE (ID)

CREATE INDEX idx2 on TESTTABLE(NAME)

-- 插入300行數據

DECLARE @i INT
SET @i = 1
WHILE (@i <= 300)
BEGIN
INSERT INTO TESTTABLE VALUES(@i, CONCAT('name_', @i))
SET @i = @i + 1
END 

備份文件損壞

對前面建立的測試數據庫做一個全備份,名爲TESTDB.BAK。因爲數據庫比較小,所以備份文件也會比較小,用二進制文件編輯器如UltraEdit更改此文件一些地方的內容,以模擬該文件受損的情形,另存爲TESTDB_BAD.BAK。執行如下命令,對數據庫進行恢復:

RESTORE DATABASE TESTDB FROM DISK='D:\temp\TESTDB_BAD.bak'

在做恢復的時候,可能會碰到如下的錯誤,並且恢復過程會中斷。

Processed 312 pages for database 'TESTDB', file 'TESTDB' on file 1.
Processed 3 pages for database 'TESTDB', file 'TESTDB_log' on file1.
Msg 3167, Level 16, State 1, Line 1
RESTORE could not start database 'TESTDB'.
Msg 3013, Level 16, State 1, Line 1
RESTORE DATABASE is terminating abnormally.
Msg 824, Level 24, State 2, Line 1
SQL Server detected a logical consistency-based I/O error: incorrectchecksum (expected: 0x9cb7ecfe; actual: 
0x6f316d79).It occurred during a readof page (1:19) in database ID 8 at offset 0x00000000026000 in file
 'C:\ProgramFiles\Microsoft SQL Server\MSSQL11.DENALI\MSSQL\DATA\TESTDB.mdf'.  Additional messages in the
 SQL Server errorlog or system event log may provide more detail. This is a severe errorcondition that 
threatens database integrity and must be corrected immediately. Complete a full database consistency 
check (DBCC CHECKDB). This error can becaused  by many factors; for more information, see SQL Server
 Books Online.

這時,可以使用WITH CONTINUE_AFTER_ERROR參數嘗試再次恢復:

RESTORE DATABASE TESTDB FROM DISK='D:\temp\TESTDB_BAD.bak'

WITH CONTINUE_AFTER_ERROR

加CONTINUE_AFTER_ERROR, 雖然備份文件受損,但是會盡最大可能來繼續恢復,顯示恢復完成。

Processed 312 pages for database 'TESTDB', file 'TESTDB' on file 1.
 
Processed 3 pages for database 'TESTDB', file 'TESTDB_log' on file1.
 
Restore was successful but deferred transactions remain. Thesetransactions cannot be resolved because there are 
data that is unavailable.Either use RESTORE to make that data available or drop the filegroups if younever need 
this data again. Dropping the filegroup results in a defunctfilegroup.
 
RESTORE WITH CONTINUE_AFTER_ERROR was successful but some damage wasencountered. Inconsistencies in the database 
are possible.
 
RESTORE DATABASE successfully processed 315 pages in 0.271 seconds(9.055 MB/sec).

不幸的是,對數據庫做完恢復後,該數據庫有可能會馬上處於Suspect(質疑)狀態。我們還是無法使用該數據庫。 我們把該數據庫設置爲緊急狀態,並執行數據庫修復命令:

ALTER DATABASE TESTDB SET EMERGENCY
DBCC CHECKDB(TESTDB)
DBCC CHECKDB(TESTDB, REPAIR_ALLOW_DATA_LOSS)
use TESTDB
SELECT * FROM TESTTABLE 

上述命令都無法執行成功,並且會報如下錯誤:

Msg 945, Level 14, State 2, Line 4

Database 'TESTDB' cannot be opened due to inaccessible files orinsufficient memory or disk space. Seethe SQL Server errorlog for details.

碰到這種情況,即使在恢復數據庫的時候使用CONTINUE_AFTER_ERROR參數,但是還是無濟於事。對於這個受損的備份文件,我們只能遺棄。

根據備份文件受損的地方不同,有時候還是能夠使用上述方法找回一部分數據的。這很大部分依賴於在備份文件中受損的位置。所以CONTINUE_AFTER_ERROR這個參數,對於受損的數據庫備份恢復,雖然是一個不錯的嘗試,但也並不是萬能的。

日誌文件損壞

下面是一個模擬日誌文件損壞的測試。請運行如下的腳本操作。第一個UPDATE語句是更新ID=295的數據,並且馬上做一個CHECKPOINT的動作,這會使得數據的更新,立刻寫入數據文件中。第二個UPDATE語句打算要更新ID=296的數據,這兩個操作是放在同一個事務內進行的。但是在第一個UPDATE語句結束後,不等WAITFOR語句結束,請馬上殺掉數據庫服務進程(Sqlservr.exe)。這樣,在數據文件裏,ID=295的記錄已經被修改,ID=296的記錄還沒被修改。如果日誌文件不損壞,下次SQLServer重新啓動時,SQL會發現這個做到一半的事物,將ID=295的記錄修改回滾。

BEGIN TRANSACTION
UPDATE TESTTABLE SET NAME = 'xxxx' where ID = 295
CHECKPOINT
WAITFOR DELAY '0:1:0'
UPDATE TESTTABLE SET NAME = 'xxxx' where ID = 296
COMMIT TRANSACTION

在這個測試裏,我們編輯該數據庫的日誌文件,人爲對數據庫的日誌文件造成損壞。當該數據庫服務重新啓動的時候,該數據庫會處於RECOVERY_PENDING狀態,查看數據庫錯誤日誌,有如下的錯誤,表明日誌文件受損。

2012-04-15 22:49:57.930 spid26s Error: 824, Severity: 24, State: 2.

2012-04-15 22:49:57.930 spid26s SQL Server detected a logicalconsistency-based I/O error: incorrect checksum (expected: 0x6c1e7b9c; actual:0xd6a87d4a). It occurred during a read of page (2:0) in database ID 8 at offset0000000000000000 in file 'C:\Program Files\Microsoft SQL Server\MSSQL11

2012-04-15 22:49:57.930 spid26s Error: 5105, Severity: 16, State: 1.

2012-04-15 22:49:57.930 spid26s A file activation error occurred. Thephysical file name 'C:\Program Files\Microsoft SQLServer\MSSQL11.DENALI\MSSQL\DATA\TESTDB_log.ldf' may be incorrect. Diagnose andcorrect additional errors, and retry the operation.

2012-04-15 22:49:57.950 spid26s File activation failure. The physicalfile name "C:\Program Files\Microsoft SQLServer\MSSQL11.DENALI\MSSQL\DATA\TESTDB_log.ldf" may be incorrect.

2012-04-15 22:49:57.950 spid26s The log cannot be rebuilt because therewere open transactions/users when the database was shutdown, no checkpointoccurred to the database, or the database was read-only. This error could occurif the transaction log file was manually deleted or lost due to a

針對這種日誌損壞,讀者可以用下面的方法,重建數據庫日誌,以恢復數據庫的正常訪問:

ALTER DATABASE TESTDB SET EMERGENCY
ALTER DATABASE TESTDB Rebuild LOG on 
(name=xxxx_log, filename='D:\temp\xxxx_log.LDF')
ALTER DATABASE TESTDB SET MULTI_USER 

在重建數據庫日誌以後,一定要運行DBCC CHECKDB以確保沒有一致性錯誤。

有一個要注意的地方,對數據庫日誌進行重建以後,我們會發現ID爲295的NAME變成了xxxx,而ID爲296的NAME依舊是name_296。如下圖10-2所示。這從數據庫的邏輯上來講是沒有問題的。但是從應用的角度,可能會大有問題。應用程序可能有邏輯需求,要求ID爲295和ID爲296的名字要麼同時改變,要麼同時不改變,在應用程序中,我們開啓了事務以確保這一點。但是經過日誌重建後,這點不能得到保證了。

因此,重建數據庫的日誌文件,是迫不得已的辦法,會破壞數據的一致性。還是建議從好的數據庫備份中恢復數據,這樣能保證數據在業務邏輯上的一致。

用戶數據文件損壞

讓我們接着模擬數據庫文件損壞的各種情形。新建測試數據庫後,把TESTDB下線,編輯該數據庫的數據文件,以模擬該數據文件受損的情形。然後把該數據庫上線,運行DBCC CHECKDB命令,會報告發現數據庫受損。根據受損的部位,有以下幾個情形:

· 非聚集索引頁面受損

具體的錯誤信息如下:

Msg 8939, Level 16, State 98, Line 1

Table error: Object ID 245575913, index ID 2, partition ID72057594039173120, alloc unit ID 72057594043564032 (type In-row data), page(1:228). Test (IS_OFF (BUF_IOERR, pBUF->bstat)) failed. Values are 133129and -4.

Msg 8928, Level 16, State 1, Line 1

Object ID 245575913, index ID 2, partition ID 72057594039173120,alloc unit ID 72057594043564032 (type In-row data): Page (1:228) could not beprocessed. See other errors for details.

Msg 8980, Level 16, State 1, Line 1

Table error: Object ID 245575913, index ID 2, partition ID72057594039173120, alloc unit ID 72057594043564032 (type In-row data). Indexnode page (1:264), slot 0 refers to child page (1:228) and previous child(0:0), but they were not encountered.

Msg 8978, Level 16, State 1, Line 1

Table error: Object ID 245575913, index ID 2, partition ID72057594039173120, alloc unit ID 72057594043564032 (type In-row data). Page(1:265) is missing a reference from previous page (1:228). Possible chainlinkage problem.

DBCC results for 'TESTTABLE'.

There are 300 rows in 2 pages for object "TESTTABLE".

CHECKDB found 0 allocation errors and 4 consistency errors in table'TESTTABLE' (object ID 245575913).

讀者會發現數據庫受損是發生在表格TESTTABLE,而且是索引編號爲2的存儲上面。索引編號大於1的都是非聚集索引。因此,讀者可以重建非聚集索引來嘗試對數據庫進行修復。

DROP INDEX idx2 ON TESTTABLE
GO
CREATE INDEX idx2 ON TESTTABLE(NAME)

再次運行DBCC CHECKDB(TESTDB)命令,檢查結果顯示,數據庫中的數據是一致的。由於受損的地方恰好在非聚集索引頁上,所以我們對數據庫可以進行不丟失數據修復,如果受損在其他地方,如聚集索引頁,則丟失數據難以避免。

· 聚集索引頁面受損

根據第八章數據庫空間管理中提到的方法,我們執行下面一系列查詢,然後對聚集索引頁面進行篡改:

SELECT * FROM sys.objects WHERE NAME = 'TESTTABLE'

-- 結果爲245575913

SELECT * FROM sys.partitions WHERE object_id = '245575913'

-- 有兩個索引,其中聚集索引所在地partition_id爲72057594039107584

SELECT * FROM sys.system_internals_allocation_units WHERE

container_id = '72057594039107584'

-- 得知第一個數據頁爲(1, 226)

DBCC TRACEON(3604)

DBCC PAGE('TESTDB', 1, 226, 1)

DBCC TRACEOFF(3604)

-- 得知數據頁的具體內容,編輯數據文件,找到相應位置,對該數據頁面的內容進行篡改

得知數據頁的具體內容後,把該數據庫下線, 根據第一個數據頁的查詢得到的字符串,編輯數據文件,對該數據頁內容進行篡改。然後上線該數據庫,並對數據庫做一致性檢查,會發現如下錯誤:

Msg 8978, Level 16, State 1, Line 1

Table error: Object ID 245575913, index ID 1, partition ID72057594039107584, alloc unit ID 72057594043498496 (type In-row data). Page(1:231) is missing a reference from previous page (1:226). Possible chainlinkage problem.

Msg 8939, Level 16, State 98, Line 1

Table error: Object ID 245575913, index ID 1, partition ID72057594039107584, alloc unit ID 72057594043498496 (type In-row data), page(1:226). Test (IS_OFF (BUF_IOERR, pBUF->bstat)) failed. Values are 133129and -4.

Msg 8928, Level 16, State 1, Line 1

Object ID 245575913, index ID 1, partition ID 72057594039107584,alloc unit ID 72057594043498496 (type In-row data): Page (1:226) could not beprocessed. See other errors for details.

Msg 8980, Level 16, State 1, Line 1

Table error: Object ID 245575913, index ID 1, partition ID72057594039107584, alloc unit ID 72057594043498496 (type In-row data). Indexnode page (1:230), slot 0 refers to child page (1:226) and previous child(0:0), but they were not encountered.

DBCC results for 'TESTTABLE'.

There are 63 rows in 1 pages for object "TESTTABLE".

CHECKDB found 0 allocation errors and 4 consistency errors in table'TESTTABLE' (object ID 245575913).

這次,數據頁受損是在聚集索引頁面上(索引編號爲1),也就是在數據頁面上,因此我們無法通過重建索引的方法對數據進行修復。我們採用REPAIR_ALLOW_DATA_LOSS的方法,對數據庫進行修復。

ALTER DATABASE TESTDB set EMGERGENCY
ALTER DATABASE TESTDB set single_user withrollback immediate
GO
DBCC CHECKDB(TESTDB, 'REPAIR_ALLOW_DATA_LOSS')
GO
ALTER DATABASE TESTDB set multi_user

檢索修復後的數據庫,讀者會發現,原來有300條記錄的,現在只剩下了63條。有237條數據在修復的過程中丟失了。這是因爲修復程序發現頁內數據有問題,採用比較保守的辦法,是把整個頁面的數據刪除,以維護數據的一致性。

· 系統頁面受損

數據庫有一些系統頁面或系統表格非常重要,如在第八章數據庫空間管理提及的PFS頁面,GAM頁面和SGAM頁面。如果這些頁面受損,那麼整個數據庫基本上很難修復。下面是一個示例。首先查詢TESTDB的GAM頁面信息,得到Slot 1裏面的數據0000381f。

把數據庫TESTDB下線,用二進制文件編輯器打開數據文件,對字符串0000381f進行篡改,然後把該數據庫上線。數據庫一上線後,TESTDB馬上處於置疑(Suspect)狀態,如圖10-5所示。

我們把該數據庫設爲緊急模式,然後執行DBCC CHECKDB(TESTDB)

ALTER DATABASE TESTDB SET Emergency

DBCC CHECKDB(TESTDB)

會報如下的錯誤信息:

DBCC results for 'TESTDB'.

Msg 8998, Level 16, State 1, Line 1

Page errors on the GAM, SGAM, or PFS pages prevent allocationintegrity checks in database ID 5 pages from (1:0) to (1:517631). See othererrors for cause.

用下面的命令嘗試對數據庫進行修復:

ALTER DATABASE TESTDB SET Emergency
ALTER DATABASE TESTDB set single_user with rollback immediate
DBCC CHECKDB(TESTDB, 'REPAIR_ALLOW_DATA_LOSS')

由於系統頁面損壞,所以即使用REPAIR_ALLOW_DATA_LOSS也無法對數據庫進行修復。修復程序提示如下錯誤:

Msg 5028, Level 16, State 4, Line 3
The system could not activate enough of the database to rebuild thelog.
DBCC results for 'TESTDB'.
CHECKDB found 0 allocation errors and 0 consistency errors indatabase 'TESTDB'.
Msg 7909, Level 20, State 1, Line 3
The emergency-mode repair failed.You must restore from backup.

因此,如果系統頁面或者系統表受損,則只能從備份當中恢復數據

10.3.4 系統數據庫損壞

系統數據庫包括master數據庫,tempdb數據庫,MSDB數據庫和MODEL數據庫。由於tempdb數據庫會在數據庫重新啓動時,再次創建,因此對於tempdb數據庫的損壞,我們可以重新啓動數據庫對其進行恢復。

master數據庫,MSDB數據庫和MODEL數據庫一般情況下不會進行很多改動,所以如果碰到系統數據庫損壞,從備份中恢復系統數據庫是最好的辦法。如果我們沒有對系統數據庫做備份,那麼修復系統數據庫的方法,則是從相同版本的數據庫上,拷貝系統數據庫的數據文件和日誌文件,然後替換受損的系統數據庫。具體方法,可參考前面章節對移動數據庫的介紹。這樣,損失的數據,包括master裏面的數據和MSDB裏面的數據。用戶可以通過重建登錄以及重建Job來進行恢復。

四. Database Mirroring和AlwaysOn的頁面自動修復功能

在SQL Server 2008Enterprise或更高版本上有一個很誘人的新功能。如果某個配置了數據庫鏡像或者AlwaysOn的數據庫發生數據頁面訪問錯誤(823、824和829),數據庫夥伴會自動嘗試解決錯誤。無法讀取頁的夥伴會從其他夥伴請求新副本。如果此請求成功,則將以新副本替換不可讀的頁,這通常會解決發生在鏡像或AlwaysOn的主服務器上的頁面損壞。

1. 數據庫鏡像的頁面自動修復功能

例如,在鏡像的主服務器上的主體數據庫第100頁上發生了824錯誤,SQL Server會到鏡像夥伴服務器上的數據庫裏,把第100頁的內容讀出來。如果鏡像夥伴服務器上的數據庫沒有損壞,這個頁面讀取請求就能夠成功。SQLServer會自動將主服務器上損壞的頁面替換成讀到的正確內容,從而達到修復頁面的目的。數據庫鏡像夥伴進行的自動頁修復不同於DBCC修復。自動頁修復會保留所有數據。相反,使用DBCC REPAIR_ALLOW_DATA_LOSS選項更正錯誤可能需要刪除某些頁(從而會刪除數據)。

由於數據庫損壞大部分和硬件有關係,而兩套硬件同時損壞的概率是很小的,所以這個功能看上去的確很誘人。

當然它也有一定的侷限性。數據庫鏡像自動頁修復只嘗試修復特定數據文件中的頁,此數據文件是指對其執行的操作由於表10-2中列出的某一錯誤而失敗的數據文件。

表 10- 2 Database Mirroring支持修復的錯誤類型

錯誤號
說明
導致自動頁修復嘗試的實例
823
僅當操作系統對數據執行循環冗餘檢查(CRC)失敗時才執行此操作 ERROR_CRC。此錯誤的操作系統值爲23
824
邏輯錯誤 邏輯數據錯誤,例如殘缺寫或錯誤的頁校驗和
829
頁已標記爲還原已掛起 全部

也不是所有的頁面都能被自動修復的。以下控制頁類型無法通過數據庫鏡像修復:

· 文件頭頁(頁ID 0)。

· 頁9(數據庫啓動頁)。

· 分配頁:全局分配映射(GAM)頁、共享全局分配映射(SGAM)頁和頁可用空間(PFS)頁。

處理主體數據庫中的I/O錯誤

在主體數據庫中,僅當節點處於SYNCHRONIZED狀態且主體服務器仍向鏡像服務器發送日誌記錄時,纔會嘗試自動頁修復。自動頁修復嘗試中操作的基本順序如下:

(1)當主體數據庫中的數據頁上發生讀取錯誤時,主體服務器將使用相應的錯誤狀態在suspect_pages表中插入一行。然後,主體服務器從鏡像服務器請求此頁的副本。此請求指定當前在刷新日誌末尾的頁ID和LSN。此頁將標記爲“還原已掛起”。這會使它在自動頁修復嘗試期間不可訪問。修復嘗試期間對此頁的訪問嘗試將失敗,並顯示錯誤829(還原已掛起)。

(2)收到頁請求後,鏡像服務器將等待,直到將日誌重做到請求中指定的LSN處。然後,鏡像服務器會嘗試訪問鏡像數據庫中的此頁。如果可以訪問此頁,則鏡像服務器將此頁的副本發送到主體服務器。否則,鏡像服務器將向主體服務器返回錯誤,並且自動頁修復嘗試失敗。

(3)主體服務器處理包含此頁新副本的響應。

(4)自動頁修復嘗試修復可疑頁後,此頁將在suspect_pages表中標記爲已還原(event_type = 4)。

(5)如果此頁I/O錯誤導致出現任何延遲的事務,則修復此頁後,主體服務器將嘗試解決這些事務。

處理鏡像數據庫中的I/O錯誤

SQL Server會用以下方式處理在鏡像數據庫中發生的數據頁I/O錯誤。

(1)如果鏡像服務器在其重做日誌記錄時遇到一個或多個頁I/O錯誤,則鏡像會話將進入SUSPENDED狀態。此時,鏡像服務器使用相應的錯誤狀態在suspect_pages表中插入一行。然後,鏡像服務器從主體服務器請求此頁的副本。

(2)主體服務器嘗試訪問主體數據庫中的此頁。如果可以訪問此頁,則主體服務器將此頁的副本發送到鏡像服務器。

(3)如果鏡像服務器收到它請求的每一頁的副本,則鏡像服務器將嘗試恢復鏡像會話。如果自動頁修復嘗試修復了可疑頁,此頁將在suspect_pages表中標記爲已還原(event_type = 4)。

2. AlwaysOn的頁面自動修復功能

AlwaysOn的頁面自動修復功能和鏡像非常相似。下面是它具體的工作過程:

主數據庫上的頁面損壞

1. 在主副本上訪問頁面,得到了823,824或829錯誤。訪問操作被中止。Suspect_pages表中會插入一條記錄來標示該質疑頁面。該頁面會被標記爲“還原/掛起”狀態,在自動修復操作完成之前(無論是修復成功還是失敗),任何訪問這個頁面的操作都會得到829錯誤。

2. 主副本發送廣播請求到所有輔助副本上,請求該頁面的副本來修復受損頁。廣播請求中會包含所需頁面的ID,以及主副本的當前LSN(被寫入到LDF文件的最後一個日誌)。

3. 當輔助數據庫將日誌重做到請求中所指定的LSN後,它就會嘗試去訪問請求中所包含的頁ID。如果成功獲得這個頁面,就把這個頁面發送給主副本。如果失敗,那麼整個自動修復就失敗,輔助副本會返回一個錯誤給主副本。因此,修復頁面所需要的時間主要取決於輔助數據需要多長時間才能追上主數據的LSN。

4. 主副本可能從多個輔助副本上都收到頁面。第一個收到的頁面會用來替代主數據庫上損壞的頁面。後續那些來自其他輔助副本的頁面就被丟棄了。

5. 當自動修復完成工作後,suspect_pages 該頁面所對應記錄的event_type 列會被設置成5。

輔助數據庫上的頁面損壞

1. 如果輔助副本在其重做日誌記錄時遇到823,824或829錯誤,那麼輔助數據庫將進入掛起狀態。 同時在輔助副本的suspect_pages 表中插入一行記錄來表示這個損壞頁面。

2. 輔助數據庫向主數據庫請求該頁的當前副本。

3. 主數據庫響應該請求將頁面副本和當前的LSN發送給輔助副本,並且只允許輔助副本上的重做線程來訪問這個頁面。

4. 輔助副本上的重做線程繼續運行。直到輔助數據庫上重做的LSN達到並超過了主數據發送來的頁面副本的LSN時,該頁面才能被輔助副本上所有的工作線程所訪問。從此,該頁面就在內存中替換了輔助數據庫中的損壞頁面。等到下次checkpoint的時候,該頁面就會被寫回到磁盤上。

每當可用性組使用自動頁面修復功能修復了一個受損頁面後,在動態管理視圖sys.dm_hadr_auto_page_repair中就會多出一條記錄,代表了該自動修復操作。你可以通過監視該動態管理視圖來了解系統中發生的自動修復情況。

所以,如果鏡像或者AlwaysOn能夠正常工作,數據庫的安全性就能夠得到很大的提升。雖然配置鏡像或者AlwaysOn會增加維護成本,但是還是建議有條件的系統試一試。

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