在產品環境中定時執行SP時,偶爾會遇到SP執行失敗的情況,SQL Server拋出的錯誤消息是:
Could not continue scan with NOLOCK due to data movement
從錯誤消息的字面意思上來理解,存儲過程執行失敗的原因,很有可能是:SQL Server正在對基礎表進行全表掃描,表帶有NOLOCK鎖提示,在掃描的當前位置缺少一個或多個數據頁。但是,缺失的數據頁並不能說明數據庫中存在損壞問題,此錯誤的根本原因是由於DML語句引起的頁面拆分導致頁面被刪除或移動,在某一個時刻,數據庫無法保證數據的一致性,導致SQL Server無法繼續掃描操作。為了驗證是否是數據頁導致的,可以使用CHECKDB命令:
DBCC CHECKDB(MY_DATABASE) WITH PHYSICAL_ONLY
如果數據庫出現故障,或者數據出現丟失,該命令會顯示出詳細的錯誤信息。
但是,通常情況下,出現數據庫故障的情況非常少,因此,該命令通常不會返回任何錯誤消息。排除掉數據庫故障這個錯誤之外,那么出現這種錯誤的原因,只有一個可能,那就是在堆表上執行SELECT命令的時候,使用了表提示(hint) with(nolock),同時有一個update命令在更新該表,增加了表中字段長度,使得某一個或某幾個數據頁被拆分。由於帶with(nolock)的select 查詢不會申請任何鎖,不會阻塞X鎖的執行,這使得update操作和select操作可以同時執行。
要解決該問題,最簡單的方案是把with(nolock) hint去掉,讓select 操作阻塞update操作的執行,也就是說,在查詢表的時候,不能更新表,就可以避免該問題的再次發生。
該問題發生的條件,我認為是以下三個條件共同作用的結果:
- 該表是一個堆表,且數據量較大,行寬較大,包含LOB類型的數據列
- 對堆表同時執行查詢操作和更新操作,並且查詢操作帶有with(nolock) hint
- 查詢操作是一個Join操作,並且連接操作(很可能是哈希join)持續的時間較長
DBCC CHECKDB是什么?
DBCC CHECKDB 的作用是檢查數據庫中所有對象的邏輯和物理完整性,這個命令實際上調用多個命令來完成完整性檢查:
- 運行DBCC CHECKALLOC 檢查數據庫中硬盤空間分配結構的一致性
- 運行DBCC CHECKTABLE 檢查構成表或索引視圖的所有頁面和結構的完整性。
- 運行 DBCC CHECKCATALOG 檢查catalog的一致性
- 驗證數據庫中每個索引視圖的內容的有效性
- 對於使用FILESTREAM,當把varbinary(max)數據存儲再文件系統中時,驗證表的元數據和文件系統(目錄和文件)之間鏈接的一致性。
DBCC CHECKDB命令的語法是:
DBCC CHECKDB [ ( database_name | database_id | 0 [ , NOINDEX | , Repair ] ) ] [ WITH [ ALL_ERRORMSGS ] [ , EXTENDED_LOGICAL_CHECKS ] [ , NO_INFOMSGS ] [ , TABLOCK ] [ , ESTIMATEONLY ] [ , { PHYSICAL_ONLY | DATA_PURITY } ] [ , MAXDOP = number_of_processors ] ]
參數注釋:
database_name | database_id | 0: 指定執行完整性檢查的數據庫,如果不指定該參數,默認值是當前的數據庫
NOINDEX:指定不對用戶表的非聚集索引進行密集檢查,這有助於減少命令整體執行的時間, NOINDEX不會影響系統表,因為始終對系統表索引執行完整性檢查。
Repair:用於指定命令如何修復發現的錯誤,僅在萬不得已時才使用REPAIR選項,指定的數據庫必須處於單用戶模式下才能使用修復選項,修復選項有三個有效值:
- REPAIR_ALLOW_DATA_LOSS:嘗試修復所有的錯誤,但是可能導致數據丟失
- REPAIR_REBUILD:執行無丟失數據的修復,此選項無法修復跟FILESTREAM 數據相關的錯誤。
- REPAIR_FAST:僅僅是向后兼容,不做任何修復
ALL_ERRORMSGS:顯示素有的錯誤消息,這是默認的設置
EXTENDED_LOGICAL_CHECKS:對索引視圖、XML索引、空間索引執行邏輯一致性檢查
NO_INFOMSGS:不顯示信息性消息
TABLOCK:使命令執行時對基礎表上表級鎖,這使得命令執行的更快,但是會降低數據庫的並發性。
ESTIMATEONLY:用於顯示運行DBCC CHECKDB所需的tempdb空間消耗的估計數量,這數量是估計的,不做實際的數據庫檢查。
PHYSICAL_ONLY:將檢查范圍限制在頁面和記錄頭部(Record Header)的物理結構的完整性,以及數據庫空間分配的一致性上,該選項用於使用相對少的開銷對數據庫物理一致性的檢查。
DATA_PURITY:指定檢查列值的范圍,列值完整性檢查默認情況下處於啟用狀態,並且不需要DATA_PURITY選項。如果指定了PHYSICAL_ONLY選項,那么不會執行列完整性的檢查。
MAXDOP:執行命令運行的最大並發程度
參考文檔:
Error 601: Could not continue scan with NOLOCK due to SQL Server data movement