備份壓縮導致 SQL 2017 TDE 數據庫損壞
在 SQL Server 2017 (CU3) 上,每當我在我的一個 TDE 數據庫上啟用備份壓縮時,備份過程總是會損壞數據庫中的特定頁面。如果我在沒有壓縮的情況下執行備份,它不會被損壞。以下是我為驗證和重現此問題所採取的步驟:
- 在數據庫“TDE_DB1”上執行 DBCC CheckDB;一切都很好,沒有錯誤;
- 無壓縮成功備份數據庫;RESTORE VERIFYONLY 說一切都很好;
- 成功恢復數據庫為“TDE_DB2”;一切都很好,DBCC CheckDB 沒有顯示錯誤;
- 成功備份“TDE_DB1”數據庫 WITH 壓縮;RESTORE VERIFYONLY 錯誤,說“檢測到備份集損壞”;
- 嘗試將數據庫恢復為“TDE_DB2”;錯誤,說“RESTORE 在數據庫中的頁面 (1:92454) 上檢測到錯誤”
- 重複步驟1-3;一切都很好;
- 刪除“TDE_DB1”和“TDE_DB2”;從備份中恢復“TDE_DB1”;一切都很好;
- 重複步驟1-5;得到相同的結果;
總結一下:數據庫和定期備份似乎很好,在數據庫上執行 CHECKDB 並在備份上執行 VERIFYONLY 不會報告任何錯誤。使用壓縮備份數據庫似乎會導致損壞。
以下是有錯誤的程式碼範例。(注意:對 TDE 數據庫使用壓縮需要 MAXTRANSFERSIZE)
-- Good, completes with no corruption; BACKUP DATABASE [TDE_DB1] TO DISK = N'E:\MSSQL\Backup\TDE_DB1a.bak' WITH CHECKSUM; RESTORE VERIFYONLY FROM DISK = N'E:\MSSQL\Backup\TDE_DB1a.bak' WITH CHECKSUM; RESTORE DATABASE [TDE_DB2] FROM DISK = 'E:\MSSQL\Backup\TDE_DB1a.bak' WITH MOVE 'DataFileName' to 'E:\MSSQL\Data\TDE_DB2.mdf' ,MOVE 'LogFileName' to 'F:\MSSQL\Log\TDE_DB2_log.ldf'; -- Bad, I haz corruption; BACKUP DATABASE [TDE_DB1] TO DISK = N'E:\MSSQL\Backup\TDE_DB1b.bak' WITH CHECKSUM, COMPRESSION, MAXTRANSFERSIZE = 131072; RESTORE VERIFYONLY FROM DISK = N'E:\MSSQL\Backup\TDE_DB1b.bak' WITH CHECKSUM; -- ERROR --Msg 3189, Level 16, State 1, Line 1 --Damage to the backup set was detected. --Msg 3013, Level 16, State 1, Line 1 --VERIFY DATABASE is terminating abnormally. RESTORE DATABASE [TDE_DB2] FROM DISK = 'E:\MSSQL\Backup\TDE_DB1b.bak' WITH MOVE 'DataFileName' to 'E:\MSSQL\Data\TDE_DB2.mdf' ,MOVE 'LogFileName' to 'F:\MSSQL\Log\TDE_DB2_log.ldf'; -- ERROR --Msg 3183, Level 16, State 1, Line 7 --RESTORE detected an error on page (1:92454) in database "TDE_DB2" as read from the backup set. --Msg 3013, Level 16, State 1, Line 7 --RESTORE DATABASE is terminating abnormally.
然後我嘗試檢查報告為有錯誤的頁面(它始終是同一頁面。),但 DBCC PAGE 報告 ObjectId 為 0。 根據 Paul Randal 的這篇文章,這意味著沒有找到元數據,並且原因之一可能是頁面本身已損壞,並且用於嘗試查找元數據的值不正確。他的建議是執行 CHECKDB,但我不能這樣做,因為損壞的備份無法恢復。
我嘗試了此 SO Post中的建議(將 INIT 和 FORMAT 添加到 BACKUP 命令)來重置元數據,但這似乎沒有改變任何東西,我仍然在壓縮備份中遇到損壞。
這只發生在我的一個 TDE 數據庫中。我在同一台伺服器上還有 4 個其他 TDE 數據庫,它們沒有這個問題。這告訴我這個特定數據庫可能存在潛在問題。我意識到簡單的解決方案就是不使用壓縮,但我覺得這實際上可能是對未來更大問題的預警。
有沒有人以前見過這個,或者知道為什麼壓縮會損壞該頁面?在這一點上,我有點不知道下一步該做什麼。我考慮從較早的備份中恢復頁面,但我認為這並不重要,因為正常數據庫中的頁面看起來不錯。
更新 1: 以下是 DBCC PAGE 的結果,選項為 0:
DBCC 執行完成。如果 DBCC 列印錯誤消息,請聯繫您的系統管理員。
頁:(1:92454)
緩衝:
BUF @0x000002187AE55640
BPAGE = 0x0000021848650000 Bhash = 0x000000000000000000
BPageno =(1:92454)BDBID = 8 Breferences = 0 Bcputicks = 563 Bsamplecount = 1
Buse1 = 51429 BStat = 0x809部落格= 0x15a Bnext = 0x15a
Bnext = 0x000000000000000000 BSTAT = 0x00000000
頁眉:
頁面@0x000002184865E000
m_pageid =(1:92454)m_headerversion = 111
m_type = 189 m_typeflagbits = 0x2d m_level = 197
m_flagbits = 0x525e m_objid(allocunitid.idobj)= 788815194
m_indexid(allocunitid.idind)= 515元數據:Allocunitid = 145011308798541824元數據:partitionData = 0元數據: IndexID = -1元數據:ObjectID = 0 m_prevpage =(32842:1881351155)m_nextpage =(13086:-560562340)
pminlen = 36067 m_slotcnt = 8149 m_freecnt = 5149 m_freeData = 7295 m_reservedcnt = 4810 m_lsn =(742012401:720884976:30191)m_xactreserved = 14755
m_xdesId = (12811:1559482793) m_ghostRecCnt = 12339
m_tornBits = -1381699202 DB Frag ID = 1
分配狀態
GAM (1:2) = 已分配 SGAM (1:3) =
未分配 PFS (1:88968) = 0x0 0_PCT_FULL DIFF (1:6) = 未更改
ML (1:7) = 未記錄
如果我嘗試使用其他選項執行 DBCC PAGE,則會收到以下錯誤:
DBCC PAGE with option 1: Msg 0, Level 11, State 0, Line 0 目前命令出現嚴重錯誤。結果,如果有的話,應該丟棄。
帶有選項 3 的 DBCC PAGE:消息 2514,級別 16,狀態 5,第 3 行發生 DBCC PAGE 錯誤:無效的頁麵類型 - 無法轉儲樣式 3。
更新 2: 以下是 sys.dm_db_database_page_allocations DMO 的一些結果:
的object_id = 75 index_id的= 1 rowset_id = 281474981625856 allocation_unit_id = 281474981625856
allocation_unit_type = 1 allocation_unit_type_desc = IN_ROW_DATA extent_file_id = 1 extent_page_id = 92448
allocated_page_iam_file_id = 1 allocated_page_iam_page_id = 104
allocated_page_file_id = 1 allocated_page_page_id = 92454
is_allocated = 0 is_iam_page = 0 is_mixed_page_allocation = 0
看起來這個問題與在其上執行了 SHRINK 操作的數據庫有關。就我而言,我在 SQL Server 2014 上複製了一個生產數據庫(已使用 TDE 加密),在數據和日誌文件上執行 DBCC SHRINKFILE,然後在我的新 SQL 上進行備份和恢復2017 伺服器。(縮小的原因是為了減小大小以更快地傳輸備份。)
作為測試,我恢復了一個未在其上執行 DBCC SHRINKFILE 的數據庫副本,並且它在壓縮備份時沒有出現損壞問題。
所以,總結一下,我的測試結果如下:
- 此“縮小”TDE 數據庫上的正常備份/恢復操作在 SQL 2017 中正常工作
- 壓縮“縮小”的 TDE 數據庫的備份似乎會導致 sys.sysmultiobjrefs 表損壞
- 壓縮正常 TDE 數據庫的備份(沒有執行 DBCC SHRINKFILE)正常工作並且不報告損壞
我不知道這是否是 SQL Server 2017 中已確認的錯誤,但我已將我的發現發送給 Microsoft 以供他們查看。
所以,這個故事的寓意是:不要收縮你的數據庫!曾經!:)