簡介
什麼是死鎖?
我認為,死鎖是由于兩個對象在擁有一份資源的情況下申請另一份資源,而另一份資源恰好又是這兩對象正持有的,導緻兩對象無法完成操作,且所持資源無法釋放。
什麼又是阻塞?
阻塞是由于資源不足引起的排隊等待現象。比如同時兩個程序去更新一個表。
這裡我們可以把阻塞作為死鎖的必要條件。下面我們先了解一下死鎖和阻塞再來看一下我最近遇到一個問題以及解決思路。
SQLServer中的死鎖
對應到SQL Server中,當在兩個或多個任務中,如果每個任務鎖定了其他任務試圖鎖定的資源,此時會造成這些任務永久阻塞,進而出現死鎖;
這些資源可能是:單行(RID,堆中的單行)、索引中的鍵(KEY,行鎖)、頁(PAG,8KB)、區結構(EXT,連續的8頁)、堆或B樹(HOBT) 、表(TAB,包括資料和索引)、檔案(File,資料庫檔案)、應用程式專用資源(APP)、中繼資料(METADATA)、配置設定單元(Allocation_Unit)、整個資料庫(DB)。
下面我簡單舉一個例子來說明一下死鎖的原理:

如圖,按步驟執行:
1. begin tran
update test1 set aaa=1
2.
begin tran
update test2 set aaa=1
update test1 set bbb=2
3.再次執行圖1中的Update test2 set bbb=2
執行完成後發現資料并未插入,且一直處于running狀态
這個時候我們通過語句查詢死鎖的程序和語句。得到如下結果:
很容易發現發生死鎖的語句,也可以使用 SQL Server Profiler 分析死鎖: 将 Deadlock graph 事件類添加到跟蹤。此事件類使用死鎖涉及到的程序和對象的 XML 資料填充跟蹤中的 TextData 資料列。SQL Server 事件探查器 可以将 XML 文檔提取到死鎖 XML 檔案中,以後可在 SQL Server Management Studio 中檢視該檔案。如圖:
接下來我們說一下如何處理死鎖
1.臨時解決方案,先Kill 掉死鎖的程序,隻是暫時解決這個問題。
2.SQL Server自動選擇一條SQL作死鎖犧牲品:當死鎖發生時,鎖螢幕線程執行死鎖檢查,資料庫引擎 選擇運作復原開銷最小的事務的會話作為死鎖犧牲品,傳回1205 錯誤,復原死鎖犧牲品的事務并釋放該事務持有的所有鎖,使其他線程的事務可以請求資源并繼續運作。
伺服器: 消息 1205,級别 13,狀态 50,行 1 事務(程序 ID xx)與另一個程序已被死鎖在 lock 資源上,且該事務已被選作死鎖犧牲品。請重新運作該事務。
3.使用SET LOCK_TIMEOUT timeout_period(機關為毫秒)來設定請求逾時。
4.在SQLServer 和程式兩個方面都可以做代碼上修正,這裡不在較長的描述,主要是通過發現死鎖等待一段時間後再次嘗試的方式來解決。
預防和避免死鎖
1.盡量減少事務執行的時間。
2.在合理的範圍内降低隔離級别。
3.同一個事務内盡量避免出現循環對同一個表的處理。
4.同一個事務内較少使用者互動,即鎖的競争。
5.盡量保證邏輯處理的順序比如對表的處理都按照一個順序進行。
6.對于需要各種邏輯處理的表,可以通過增加索引的方式來減少鎖的競争。
7.盡量減少非聚集索引的include 的列,也能減少外鍵死鎖的發生。
8.同一個對象盡量采用select 在update 前來使用。
9.對于實時性要求不高的可以使用with(nolock)來實作對表的查詢,但是可能會差生髒讀。
總結
本文簡單的介紹了死鎖的原因,如何解決和預防。當然任何事情都是雙刃劍,還要我們根據實際情況來合理減少死鎖和阻塞的發生;對于不同隔離界别鎖帶來的問題可以看一下我之前的一篇關于鎖的介紹。希望對死鎖發生預防和解決有一定的幫助。