在今天的文章里,我想談下SQL Server里如何處理死鎖。當2個查詢彼此等待時會發生死鎖,沒有一個查詢可以繼續它們的操作。首先我想給你大致講下SQL Server如何處理死鎖。最后我會展示下SQL Sever里特定的死鎖類型,還有你如何避免和解決它們。
死鎖處理
死鎖的好處是SQL Server自動檢測並解決它們。為了解決死鎖,SQL Server需要回滾2個事務中最便宜的那個。在SQL Server上下文中,最便宜的事務是寫入事務日志更少字節的那個。
SQL Server在后台進程中實現死鎖檢測稱為死鎖監控(Deadlock Monitor)。這個后台進程每5秒鍾運行一次,為死鎖檢查當前鎖定情況。在最壞的情況中,因此一個死鎖不應該超過5秒。這個查詢會回滾並收到1205錯誤號。死鎖的好事是你可以完整從錯誤情況下還原,不需要用戶的任何干預。一個聰明的開發者必須按下列步驟來從死鎖中恢復:
- 當異常拋出時,檢查1205錯誤號
- 暫時停止程序,給其他查詢時間來完成它的事務並釋放它獲得的鎖
- 重新提交查詢,即被SQL Server回滾的。
重新提交查詢后,這個查詢應該繼續執行,沒有任何問題,因為其它查詢已經完成它的事務。當然你應該保持再次發生死鎖的跟蹤,這樣的話,你不用反復重試你的事務。
你可以用多種方法來故障排除死鎖。SQL Server Profiler提供Deadlock Graph事件,一旦死鎖檢測到就會發生。如果你在SQL Server 2008或更高,你可以使用故障排除來故障排除死鎖場景。擴展事件提供你system_health事件會話,它跟蹤自SQL Server上次重啟后發生過的死鎖。還有啟用1222跟蹤標記,SQL Server會把死鎖信息寫入錯誤日志。
死鎖類型
在SQL Server里會發生各種類型的死鎖。在這一部分我想進一步談下最常見的幾個。
幾乎每個SQL Server我看到最典型的的死鎖是著名的書簽查找死鎖,當你有同時對聚集和非聚集索引讀寫活動時發生。它基本上是因為不好的索引設計造成的。在我作日常SQL Server的故障排除重,我可以說所有的死鎖,至少有90%可以通過更好的索引設計來解決。書簽查找死鎖可以通過提供覆蓋非聚集索引輕松解決。
另一個常見的死鎖是所謂的循環死鎖(Cycle Deadlock),這里你的每個查詢用不同的順序訪問表。為了避免這個特定的死鎖,你要確保每個查詢用同樣的順序訪問表。在SQL Server里會發生的最有意思的死鎖是所謂的內部並行死鎖(Intra-Parallelism Deadlock),這里平行的運算符在各自的線程內部死鎖。下圖展示了一個典型死鎖圖。
圖片本身就是一個藝術品,它因觸發SQL Server里的BUG而發生。遺憾的是,這個BUG不會被微軟修正,因為它引入回歸的可能性。因此你要確保引起這個死鎖的查詢,要在SQL Server里單線程運行。你可以通過多個選項來實現單線程執行:
- 在你的索引策略上下功夫,這樣的話查詢的花費會在當前並行閾值下(默認為5)
- 使用MAXDOP 1查詢提示,來提示SQL Server以單線程運行你的問題查詢
另一個死鎖的特效療法是啟用樂觀並發,尤其是提交讀快照隔離(Read Committed Snapshot Isolation (RCSI)),他對你的程序是完全透明的。使用樂觀並發,共享鎖消失,這意味着在SQL Server里你可以避免大量的典型鎖。
小結
死鎖是SQL Server通過回滾最便宜的事務自動處理。然而你要盡可能小的確保死鎖,因為每個回滾的死鎖都會給你的終端用戶帶來不好的影響。死鎖可以通過好的索引策略來避免,另外使用樂觀並發也是應付它們的特效葯。
感謝關注!
原文鏈接
https://www.sqlpassion.at/archive/2017/02/20/how-to-handle-deadlocks-in-sql-server