大家好,回應回到性能調優培訓。今天討論SQL Server里的死鎖(Deadlocking),第5個月的培訓就結束了。當2個查詢彼此等待,沒有查詢可以繼續它的工作就會發生死鎖。第一步我會概括介紹下SQL Server如何處理死鎖。最后我會向你展示下SQL Server里一些特定死鎖,還有你如何避免并解決它們。
死鎖處理(Deadlock Hanging)死鎖的好處是SQL Server會自動檢測并解決它們。為了解決死鎖,SQL Server需要回滾最便宜的2個事務。在SQL Server上下文里,最便宜的事務是寫入事務日志,字節數更少的事務。
SQL Server在后臺進程里使用所謂的Deadlock Monitor來實現死鎖檢測。這個后臺進程每5秒鐘運行一次,為死鎖檢查當前的死鎖情況。最壞的情況,因此死鎖應該不會超過5秒鐘?;貪L的查詢收到1205號錯誤。死鎖的"好處"是你可以從那個錯誤情形里完全還原,而不需要用戶互動。一個聰明的開發人員必須做下列事情來從死鎖中恢復:
查詢重新提交后,查詢應該繼續沒有問題,因為其它阻塞的查詢已經完成它的事務。當然,你應該保持再次死鎖的跟蹤,這樣的話,你就不用反復重試你的事務。
你可以在不同方式里故障排除死鎖。SQL Server PRofiler提供你Deadlock Graph事件,死鎖一旦發生就會檢測到。如果你在SQL Server 2008及更高的版本,你也可以用擴展事件(Extended Events)來故障排除死鎖情形。擴展時間提供你system_health事件會話,它更總自上次SQL Server重啟后發生過的死鎖。使用啟用1222跟蹤標志(trace flag),SQL Server會把死鎖信息寫入錯誤日志。
死鎖類型在SQL Server里可以發生多個類型的死鎖。在這個部分我想詳細談下最常見的幾個。
幾乎在每個SQL Server里都會看到的一個經典死鎖是著名的書簽查找死鎖(Bookmark Lookup Deadlock),當你同時對聚集和非聚集索引進行讀寫是會發生。這個是主要是因為不好的索引設計的死鎖。在我的日常生活里,作為SQL Server的故障排除者,我可以說至少所有死鎖的90%可以通過應用更好的索引設計到你工作中來避免。書簽查找死鎖可以通過第8周性能調優培訓里介紹的提供覆蓋非聚集索引來輕松剔除。
另一個常見的死鎖是所謂的循環死鎖(Cycle Deadlock),在你的各個查詢以不同的順序訪問表里發生。為了避免那個特定死鎖,你總要確保查詢在同樣的順序訪問表。另外在SQL Server里可以發生的“最好”死鎖是所謂的內部并行死鎖(Intra-Parallelism Deadlock),在并行運算符(Distribute Streams, Gather Streams, Repartition Streams)已經在各自線程間內部死鎖。下圖顯示了一個典型的死鎖圖。
圖片本身就是個很高雅的藝術品(pure art),因為你命中SQL Server里的1個BUG才發生。遺憾的是這樣的BUG微軟不會去修正,因為會引入回歸(regressions)的可能。因此你要確保引起這個死鎖的查詢,在SQL Server里都是單線程運行的。你可以通過多個選項來獲得執行計劃的單線程執行:
另一個死鎖的特效療法(miracle cure)是啟用樂觀并發控制(optimistic concurrency),尤其是讀提交快照隔離(Read Committed Snapshot Isolation (RCSI)),這個2個星期前就已經討論過,它對你的程序是完全透明的(completely transparent)。使用樂觀并發控制,共享鎖(S)就消失了,這意味這你可以剔除SQL Server里大量的典型死鎖。
小結死鎖通過SQL Server回滾最便宜的事務來自動處理。但是你必須確保死鎖盡可能小,因為每個回滾的事務都會負面影響你的終端用戶。 死鎖可以通過好的索引設計來避免,使用樂觀并發控制對它們也可以是特效療法。
下個星期,我們會開始性能調優培訓的最后一個月,我們會討論SQL Server里性能調優和故障排除的一切。請和我一起期待火力全開的最后一個月!
新聞熱點
疑難解答