一些基礎知識
要理解SQL Server中的死結,更好的方式是通過類比從更大的面理解死結。比如說一個經典的例子就是汽車(主體)對於道路(資源)的徵用,如圖1所示。
對於死結的直觀理解
在圖1的例子中,每隊汽車都佔有一條道路,但都需要另外一隊汽車所佔有的另一條道路,因此互相阻塞,誰都無法前行,因此造成了死結。由這個簡單的例子可以看出,發生死結需要四個必要條件,如下:
1)互斥條件:
主體對於資源是獨佔的,圖1中每條汽車道只能跑一隊汽車,不能跑第二隊。
2)請求和等待條件:
指主體已經保持至少一個資源,但又提出了新的資源請求,而該資源已被其它主體佔有,此時請求主體阻塞,但又對自己已獲得的其它資源保持不放。在圖1中,每隊汽車已經佔有了一條車道,又想獲得另一條由其它車隊佔有的車道,造成阻塞。
3)不剝奪條件
指的是主體已經獲得的資源在完成其目標之前不能被釋放。在圖1中,目標指的是汽車可以通過車道,不剝奪指的是在完成這個目標之前,車隊並不會讓出其已佔的車道。
4)環路等待條件
指在發生死結時,必然存在一個主體——資源的環形鏈,即主體集合{P0,P1,P2,···,Pn}中的P0正在等待一個P1佔用的資源;P1正在等待P2佔用的資源,……,Pn正在等待已被P0佔用的資源。在圖1中可以看出,四條車道和四隊汽車正好符號環路等待的條件,車隊1希望獲得車隊2佔有的車道,車隊2希望獲得車隊3佔有的車道,車隊3希望獲得車隊4佔有的車道,車隊4反過來又希望獲得車隊1佔有的車道,形成一個環路。
死結在進程中的定義
下面讓我們再縮小死結的範圍,回到電腦的世界。在電腦中,主體的這個抽象的詞被更具體的進程所替代,而資源縮小到電腦所使用的資源。在電腦中,死結是由阻塞所引起。因此在開始之間,我想簡單介紹一下進程的幾種狀態,如果有興趣,也可以參看我之前的一篇文章:http://www.cnblogs.com/CareySon/archive/2012/05/04/ProcessAndThread.html.
簡單來說,進程是組織資源的最小單位,多道作業系統中允許並發進行,每一道進程都像圖1所示的汽車那樣,需要前進,在前進的過程中,需要各種資源以及CPU,圖2是不考慮進行建立銷毀等狀態,簡單概述進程的幾種狀態。
.進程的幾種狀態
很多資源是可以共用的,比如記憶體。但對於印表機等資源來說就需要獨佔。圖2中的幾種狀態簡單理解是,當進程沒有所需的資源時,比如說等待IO,等待印表機,這時是阻塞狀態。而當進程獲得了這些資源時,就可以變為就緒狀態,在就緒狀態的進程再獲得CPU時,就變為執行狀態。而執行的過程中,CPU被剝奪了就繼續變為就緒狀態,或是當需要其它資源時,就會繼續變為阻塞狀態。以此往複。
在作業系統中,有些資源可以是不可剝奪資源,比如印表機,當印表機被一個進程佔用時,另一個進程就會被阻塞。還有一類資源是要重點強調的,這類資源是臨時性資源,比如進程產生的訊號量,訊息,緩衝區內的訊息,多個進程或線程訪問這類資源時更容易引起死結。在SQL Server中產生的死結其實就是由這類資源所造成的。
當兩個或多個進程既然有了當前的資源,又需要額外的資源時,滿足了上面所述死結的四個條件時,就會產生死結。
死結在SQL Server中的定義
在SQL Server中,阻塞更多的是產生於實現並發之間的隔離性。為了使得並發串連所做的操作之間的影響到達某一期望值而對資源人為的進行加鎖(鎖本質其實可以看作是一個標誌位)。當一個串連對特定的資源進行操作時,另一個串連同時對同樣的資源進行操作就會被阻塞(當然了,這和鎖之間的相容性有關,關於鎖更深入的討論超出了本文的範圍,關於這部分內容可以看我的另一篇文章:T-SQL查詢進階—理解SQL Server中的鎖),阻塞是死結產生的必要條件。
下面,我們通過一個簡單的例子來看死結。
首先,要出現死結,一定要滿足前面提到死結出現的四個必要條件,圖3中可以清楚的看到這兩個串連(SPID52和SPID55)是如何滿足這四個條件的。
Lock Monitor
圖3中死結後可以看到,SQL Server並不會讓死結僵持下去,而是通過一個叫Lock Monitor的線程定期進行檢測(預設是5秒)。當發現死結後,會剝奪其中一個SPID佔有的資源,好讓另一個SPID執行下去,具體剝奪哪一個SPID基於如下兩個因素:
1.死結的優先順序。
2.在死結優先順序相同的情況下,根據開銷,開銷小的事務將會被剝奪
下面,還是根據圖3中的例子,我們設定死結優先順序,使得左邊的事務被剝奪復原,如圖4所示。
.設定死結優先順序後,優先順序低的SPID被剝奪
SQL Server中死結的檢測
首先要理解,在多並發的環境中,死結是不可避免的,只能盡量的通過合理的資料庫設計,良好的索引,適當的查詢語句以及隔離等級來盡量的減少。因此,檢測死結的目的是知道哪裡可能會產生死結,通過對檢測到的死結進行分析後,盡量的最佳化查詢/索引/隔離等級來降低死結發生的可能性。
查看死結有兩種方式,一種是通過服務端的Trace來做,另一種是通過SQL Profiler,首先讓我們來看通過Trace來抓死結。