通過 SQL Server Profiler來 監(jiān)視 分析 死鎖 在兩個或多個SQL Server進(jìn)程中,每一個進(jìn)程鎖定了其他進(jìn)程試圖鎖定的資源,就會出現(xiàn)死鎖,例如, 進(jìn)程process1對table1持有1個排它鎖(X),同時process1對table2請求1個排它鎖(X), 進(jìn)程process2對table2持有1個排它
在兩個或多個SQL Server進(jìn)程中,每一個進(jìn)程鎖定了其他進(jìn)程試圖鎖定的資源,就會出現(xiàn)死鎖,例如,
進(jìn)程process1對table1持有1個排它鎖(X),同時process1對table2請求1個排它鎖(X),
進(jìn)程process2對table2持有1個排它鎖(X),同時process2對table1請求1個排它鎖(X)
類似這種情況,就會出現(xiàn)死鎖,除非當(dāng)某個外部進(jìn)程斷開死鎖,否則死鎖中的兩個事務(wù)都將無限期等待下去。
Microsoft SQL Server 數(shù)據(jù)庫引擎死鎖監(jiān)視器定期檢查陷入死鎖的任務(wù)。
如果監(jiān)視器檢測到循環(huán)依賴關(guān)系,將選擇其中一個任務(wù)作為犧牲品(通常是選擇占資源比較小的進(jìn)程作為犧牲品),然后終止其事務(wù)并提示錯誤1205。
這里我們通過SQL Server Profiler來監(jiān)視分析死鎖的發(fā)生過程,那樣我們就會深刻理解死鎖的成因。
1.創(chuàng)建測試表。
在 Microsoft SQL Server Management Studio上,新建一個查詢,寫創(chuàng)建表DealLockTest_1 & DealLockTest_2兩個表:
腳本:
代碼 Code highlighting produced by Actipro CodeHighlighter (freeware)http://www.CodeHighlighter.com/-->use Test --創(chuàng)建分析死鎖使用到的兩個表DealLockTest_1 & DealLockTest_2 go Set Nocount On Go if object_id('DealLockTest_1') Is Not Null Drop Table DealLockTest_1 go Create Table DealLockTest_1 ( ID int Identity(1,1) Primary Key, Name nvarchar(512) ) if object_id('DealLockTest_2') Is Not Null Drop Table DealLockTest_2 go Create Table DealLockTest_2 ( ID int Identity(1,1) Primary Key, Name nvarchar(512) ) Go --插入一些測試數(shù)據(jù) Insert Into DealLockTest_1(Name) Select name From sys.all_objects Insert Into DealLockTest_2(Name) Select name From sys.all_objects Go
創(chuàng)建好表和插入測試數(shù)據(jù)后,先執(zhí)行腳本代碼(因?yàn)槲覀儾恍枰櫾摯a),緊接著,我們就模擬兩個會話,一個會話里面包含一個事務(wù)。這里我們就新建兩個查詢,其中第一個會話,是更新DealLockTest_1表后,等待5秒鐘,更新DealLocktest_2.
Use Test Go --第一個會話 Begin Tran Update DealLockTest_1 Set Name=N'test1' Where ID >0 /*這里的Waitfor等待,是為了容易獲取死鎖的發(fā)生*/ Waitfor Delay '00:00:05' Update DealLockTest_2 Set Name=N'test2' Where ID >0 Commit Tran Go
代碼寫好后,我們先不要執(zhí)行代碼,接下來就寫第二個會話代碼; 第二個會話更新表的順序,剛好與第一個會話相反,是更新DealLockTest_2表后,等待5秒鐘,更新DealLocktest_1.
Use Test Go --第二個會話 Begin Tran Update DealLockTest_2 Set Name=N'test1' Where ID >0 /*這里的Waitfor等待,是為了容易獲取死鎖的發(fā)生*/ Waitfor Delay '00:00:05' Update DealLockTest_1 Set Name=N'test2' Where ID >0 Commit Tran Go
第二個會話代碼,也先不要執(zhí)行。
2.啟動SQL Server Profiler,創(chuàng)建Trace(跟蹤).
啟動SQL Server Profiler工具(在Microsoft SQL Server Management Studio的工具菜單上就發(fā)現(xiàn)它),創(chuàng)建一個Trace,Trace屬性選擇主要是包含:
Deadlock graph
Lock: Deadlock
Lock: Deadlock Chain
RPC:Completed
SP:StmtCompleted
SQL:BatchCompleted
SQL:BatchStarting
點(diǎn)執(zhí)行按鈕,啟動Trace。
3.執(zhí)行測試代碼&監(jiān)視死鎖。
轉(zhuǎn)到 Microsoft SQL Server Management Studio界面,執(zhí)行第一個會話&第二個會話的代碼,稍稍等待5秒鐘,我們就會發(fā)現(xiàn)其中一個會話收到報錯消息
我們再切換到SQL Server Profiler界面,就能發(fā)現(xiàn)SQL Server Profiler收到執(zhí)行腳本過程發(fā)生死鎖的信息。
OK,這里就先停止SQL Server Profiler上的“暫停跟蹤” Or "停止跟蹤"按鈕,下面我們具體分析死鎖發(fā)生過程。
4.分析死鎖
如下圖,我們可以看到第一個會話在SPID 54,第二個會話在SPID 55,一旦SQL Server發(fā)現(xiàn)死鎖,它就會確定一個優(yōu)勝者,可成功執(zhí)行,和另一個作為犧牲品,要回滾。
可以到看到EventClass列中,兩條SQL:BatchCompleted事件緊跟在Lock:DealLock后面,其中一條,它就是作為犧牲品,它會被回滾.而另一條SQL:BatchCompleted將會是優(yōu)勝者,成功執(zhí)行。
那么,誰是優(yōu)勝者,誰是犧牲品呢? 不用著急,通過DealLock graph事件,所返回來的信息,我們可以知道結(jié)果。
我們雖然不能明白DealLock graph圖示的含義,但通過圖中描述的關(guān)系,我們知道一些有用的信息。圖中左右兩旁橢圓形相當(dāng)一個處理節(jié)點(diǎn)(Process Node),當(dāng)鼠標(biāo)移動到上面的時候,可以看到內(nèi)部執(zhí)行的代碼,如Insert,UPdate,Delete.有打叉的左邊橢圓形就是犧牲者,沒有打叉的右邊橢圓形是優(yōu)勝者。中間兩個長方形就是一個資源節(jié)點(diǎn)(Resource Node),描述數(shù)據(jù)庫中的對象,如一個表、一行或一個索引。在我們當(dāng)前的實(shí)例中,資源節(jié)點(diǎn)描述的是,在聚集索引請求獲得排它鎖(X)。橢圓形與長方形之間,帶箭頭的連線表示,處理節(jié)點(diǎn)與資源節(jié)點(diǎn)的關(guān)系,包含描述鎖的模式.
接下來我們更詳細(xì)的看圖里面的數(shù)據(jù)說明。
先看右邊作為優(yōu)勝者的這橢圓形,我們可以看到內(nèi)容包含有:
服務(wù)器進(jìn)程 ID: 服務(wù)器進(jìn)程標(biāo)識符 (SPID),即服務(wù)器給擁有鎖的進(jìn)程分配的標(biāo)識符。這些數(shù)據(jù)描述,對于我們理解死鎖,只需要知道其中的一些就夠,除非我們在專門SQL Server機(jī)構(gòu)工作,才可能要深入理解它們。
下面我們來看左邊作為犧牲品的這橢圓形處理節(jié)點(diǎn),它告訴我們以下信息:
1.它是一個失敗的事務(wù)。(藍(lán)色的交叉表示)
2.它是作為犧牲品的T-SQL代碼。
3.它對右下方的資源節(jié)點(diǎn)有一個排它鎖(X).
4.它對右上方的資源節(jié)點(diǎn)請求 一個排它鎖(X).
我們再來看中間兩個長方形的資源節(jié)點(diǎn),兩個處理節(jié)點(diǎn)對它們各自都使用權(quán),來執(zhí)行它們各自的代碼,同時又有對對方使用資源請求的動作,從而發(fā)生了資源的競爭。
這也就讓我們明白死鎖發(fā)生的原因。
這里說明下資源節(jié)點(diǎn)的一些信息:
HoBT: 堆或 B 樹。 用于保護(hù)沒有聚集索引的表中的 B 樹(索引)或堆數(shù)據(jù)頁的鎖
associated objid: 關(guān)聯(lián)的對象ID,這里只是索引關(guān)聯(lián)的對象ID.
Index name:索引名
讓我們再對SQL Server Profiler監(jiān)視到的數(shù)據(jù),作一次整理:
回顧圖:
1.在第3行SQL:BatchStarting, SPID 54 (第一個會話啟動),在索引PK__DealLock__3214EC274222D4EF獲得一個排它鎖,再處理等待狀態(tài),(因?yàn)樵谶@個實(shí)例中我設(shè)置了Waitfor Delay '00:00:05')
2.在第6行SQL:BatchStarting, SPID 55 (第二個會話啟動),在索引PK__DealLock__3214EC2745F365D3獲得一個排它鎖,再處理等待狀態(tài),(因?yàn)樵谶@個實(shí)例中我設(shè)置了Waitfor Delay '00:00:05')
3.兩個進(jìn)程都各自獲得一個排它鎖(X),幾秒過去,它們就開始請求排它鎖。
SPID 54 (第一個會話),先對PK__DealLock__3214EC2745F365D3請求一個排它鎖(X),但PK__DealLock__3214EC2745F365D3當(dāng)前已經(jīng)給SPID 55 (第二個會話)獲得。SPID 54要于等待。
同時,
SPID 55 (第二個會話),開始對PK__DealLock__3214EC274222D4EF請求一個排它鎖(X),但PK__DealLock__3214EC274222D4EF當(dāng)前已經(jīng)給SPID 54 (第一個會話)獲得。SPID 55要等待。
這里就出現(xiàn)了進(jìn)程阻塞,從而發(fā)生死鎖。
4.SQL Server 檢查到這兩個進(jìn)程(第一個&第二個會話)發(fā)生死鎖,并對占用資源比較少的進(jìn)程,列入犧牲品名單,將它終止(Kill)。通過左右橢圓形進(jìn)程節(jié)點(diǎn)顯示,可以發(fā)現(xiàn)已用日志最少的是左邊的進(jìn)程節(jié)點(diǎn)。
5. SPID 54 (第一個會話)被回滾(Rollback),SPID 55 (第二個會話)執(zhí)行成功。
到這里我們已算完成了,對死鎖的監(jiān)視和分析。
(注:是于其他死鎖的定義,死鎖模式,死鎖避免&預(yù)防,等等,不是本文重點(diǎn),我沒有提出,網(wǎng)上太多這方面的文章)
(完)
聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com