模擬死鎖及案例分析
草稿箱二十篇隨筆沒有釋出,零零散散記錄著曾經以為還不錯的知識點。稍作整理髮布,方便以後檢視。2015-11-26 18:04 整理,未釋出
1、模擬死鎖
首先建立測試資料,然後開啟必要的跟蹤,最後執行兩個語句模擬死鎖。
1.1、建立測試資料
建立測試資料表、建立索引
create table testklup ( clskey int not null, nlskey int not null, cont1 int not null, cont2 char(3000) ) create unique clustered index inx_cls on testklup(clskey)View Codecreate unique nonclustered index inx_nlcs on testklup(nlskey) include(cont1) insert into testklup select 1,1,100,'aaa' insert into testklup select 2,2,200,'bbb' insert into testklup select 3,3,300,'ccc'
在測試之前,先開啟監視死鎖的開關1222,讓SQL Server遇到死鎖時,在Errorlog檔案裡打印出死鎖的詳細內容。
DBCC TRACEON(1222,-1)
也可以同時使用SQL Trace來捕捉和死鎖相關的資訊。主要選擇事件Locks->Daedlock graph
1.2、開啟死鎖會話
開啟一個會話進行修改
----模擬高頻update操作 declare @i int set @i = 100 while 1=1 begin update testklup set cont1 = @i where clskey = 1 set @i = @i+1 endView Code
開啟另外一個會話進行查詢
----模擬高頻select操作 declare @cont2View Codechar(3000) while 1=1 begin select @cont2=cont2 from testklup where nlskey=1 end
兩條語句一起執行,無須多長時間就會有其中一個連線遇到死鎖的錯誤:
2、分析死鎖
在開啟必要的跟蹤並捕獲到死鎖後,就可以對其進行分析。
2.1、1222標誌
這時在Errorlog檔案裡,就會看到下面的輸出
spid16s deadlock-list spid16s deadlock victim=process5f3000 spid16s process-list spid16s process id=process5f3000 taskpriority=0 logused=0 waitresource=KEY: 8:72057594040025088 (8194443284a0) waittime=3300 ownerId=16991311 transactionname=SELECT lasttranstarted=2016-09-28T15:07:26.720 XDES=0xe291448 lockMode=S schedulerid=4 kpid=5748 status=suspended spid=57 sbid=0 ecid=0 priority=0 trancount=0 lastbatchstarted=2016-09-28T15:07:26.717 lastbatchcompleted=2016-09-28T15:02:29.293 clientapp=Microsoft SQL Server Management Studio - 查詢 hostname=LIXUANYAO hostpid=7584 loginname=LIXUANYAO\Administrator isolationlevel=read committed (2) xactid=16991311 currentdb=8 lockTimeout=4294967295 clientoption1=671090784 clientoption2=390200 spid16s executionStack spid16s frame procname=adhoc line=5 stmtstart=134 stmtend=232 sqlhandle=0x02000000c3289f0a3b1699a92a155bf6611d1a82d2dbd79a spid16s select @cont2=cont2 from testklup where nlskey=1 spid16s inputbuf spid16s ----模擬高頻select操作 spid16s declare @cont2 char(3000) spid16s while 1=1 spid16s begin spid16s select @cont2=cont2 from testklup where nlskey=1 spid16s end spid16s process id=process5f3558 taskpriority=0 logused=228 waitresource=KEY: 8:72057594040090624 (8194443284a0) waittime=3300 ownerId=16991312 transactionname=UPDATE lasttranstarted=2016-09-28T15:07:26.720 XDES=0xe291860 lockMode=X schedulerid=4 kpid=7240 status=suspended spid=55 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2016-09-28T15:07:24.843 lastbatchcompleted=2016-09-28T15:07:23.603 clientapp=Microsoft SQL Server Management Studio - 查詢 hostname=LIXUANYAO hostpid=7584 loginname=LIXUANYAO\Administrator isolationlevel=read committed (2) xactid=16991312 currentdb=8 lockTimeout=4294967295 clientoption1=671090784 clientoption2=390200 spid16s executionStack spid16s frame procname=adhoc line=6 stmtstart=140 stmtend=248 sqlhandle=0x0200000087695c3ac89c30f0ca649915b3e2cb1c22db1292 spid16s update testklup set cont1 = @i where clskey = 1 spid16s inputbuf spid16s ----模擬高頻update操作 spid16s declare @i int spid16s set @i = 100 spid16s while 1=1 spid16s begin spid16s update testklup set cont1 = @i where clskey = 1 spid16s set @i = @i+1 spid16s end spid16s resource-list spid16s keylock hobtid=72057594040025088 dbid=8 objectname=Test.dbo.testklup indexname=inx_cls id=locka2bc5c0 mode=X associatedObjectId=72057594040025088 spid16s owner-list spid16s owner id=process5f3558 mode=X spid16s waiter-list spid16s waiter id=process5f3000 mode=S requestType=wait spid16s keylock hobtid=72057594040090624 dbid=8 objectname=Test.dbo.testklup indexname=inx_nlcs id=locka2b8d80 mode=S associatedObjectId=72057594040090624 spid16s owner-list spid16s owner id=process5f3000 mode=S spid16s waiter-list spid16s waiter id=process5f3558 mode=X requestType=waitView Code
先來看看這些輸出是什麼意思,完成下表:
參與者 | process5f3000 | process5f3558 |
---|---|---|
犧牲者 | √ | |
SPID | 57 | 55 |
連線背景 |
Microsoft SQL Server Management Studio - 查詢 hostname=LIXUANYAO hostpid=7584 loginname=LIXUANYAO\Administrator |
Microsoft SQL Server Management Studio - 查詢 hostname=LIXUANYAO hostpid=7584 loginname=LIXUANYAO\Administrator |
正在申請中資源/型別 | KEY: 8:72057594040025088 (8194443284a0)/ Mode:S | KEY: 8:72057594040090624 (8194443284a0)/ Mode:X |
當前開啟了幾層事務 | 0 | 2 |
事務隔離級別 | read committed (2) | read committed (2) |
當前正在執行的語句 | select @cont2=cont2 from testklup where nlskey=1 | update testklup set cont1 = @i where clskey = 1 |
當前正在執行的批處理 |
--模擬高頻select操作 declare @cont2 char(3000) while 1=1 begin select @cont2=cont2 from testklup where nlskey=1 end |
--模擬高頻update操作 declare @i int set @i = 100 while 1=1 begin update testklup set cont1 = @i where clskey = 1 set @i = @i+1 end |
死鎖資源 | 資源2 | 資源1 |
資源型別 | keylock | keylock |
具體內容 |
hobtid=72057594040090624 dbid=8 objectname=Test.dbo.testklup indexname=inx_nlcs id=locka2b8d80 |
hobtid=72057594040025088 dbid=8 objectname=Test.dbo.testklup indexname=inx_cls id=locka2bc5c0 |
持有資源程序/型別 | process5f3000(spid57) / mode:S | process5f3558(spid55) / mode:X |
等待資源程序/型別 | process5f3558(spid55) / mode:X | process5f3000(spid57) / mode:S |
2.2、死鎖圖
在跟蹤檔案裡,可以看到這樣的死鎖圖形:
我們可以從跟蹤檔案中,提取死鎖事件資料,將死鎖圖儲存為死鎖XML檔案(.xdl)。然後針對.xdl檔案使用記事本開啟,整個死鎖的內容一目瞭然。
/** 死鎖事件資料是後期提取的,與原死鎖圖稍有不同 **/ -- 第一部分 <deadlock-list> <deadlock victim="process2b6b000"> -- 第二部分 <process-list> <process id="process2b6b000" taskpriority="0" logused="0" waitresource="KEY: 8:72057594040025088 (8194443284a0)" waittime="636" ownerId="758239" transactionname="SELECT" lasttranstarted="2016-10-26T09:59:00.300" XDES="0x5dba408" lockMode="S" schedulerid="1" kpid="4008" status="suspended" spid="57" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2016-10-26T09:59:00.300" lastbatchcompleted="2016-10-26T09:58:55.807" clientapp="Microsoft SQL Server Management Studio - 查詢" hostname="LIXUANYAO" hostpid="5872" loginname="LIXUANYAO\Administrator" isolationlevel="read committed (2)" xactid="758239" currentdb="8" lockTimeout="4294967295" clientoption1="671090784" clientoption2="390200"> <executionStack> <frame procname="adhoc" line="5" stmtstart="134" stmtend="232" sqlhandle="0x02000000c3289f0a3b1699a92a155bf6611d1a82d2dbd79a"> select @cont2=cont2 from testklup where nlskey=1 </frame> </executionStack> <inputbuf> ----模擬高頻select操作 declare @cont2 char(3000) while 1=1 begin select @cont2=cont2 from testklup where nlskey=1 end </inputbuf> </process> <process id="process2dc2aa8" taskpriority="0" logused="228" waitresource="KEY: 8:72057594040090624 (8194443284a0)" waittime="636" ownerId="758240" transactionname="UPDATE" lasttranstarted="2016-10-26T09:59:00.307" XDES="0x5bf6c08" lockMode="X" schedulerid="4" kpid="5832" status="suspended" spid="55" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2016-10-26T09:58:59.470" lastbatchcompleted="2016-10-26T09:58:58.073" lastattention="2016-10-26T09:58:58.073" clientapp="Microsoft SQL Server Management Studio - 查詢" hostname="LIXUANYAO" hostpid="5872" loginname="LIXUANYAO\Administrator" isolationlevel="read committed (2)" xactid="758240" currentdb="8" lockTimeout="4294967295" clientoption1="671090784" clientoption2="390200"> <executionStack> <frame procname="adhoc" line="6" stmtstart="140" stmtend="248" sqlhandle="0x0200000087695c3ac89c30f0ca649915b3e2cb1c22db1292"> update testklup set cont1 = @i where clskey = 1 </frame> </executionStack> <inputbuf> ----模擬高頻update操作 declare @i int set @i = 100 while 1=1 begin update testklup set cont1 = @i where clskey = 1 set @i = @i+1 end </inputbuf> </process> </process-list> -- 第三部分 <resource-list> <keylock hobtid="72057594040025088" dbid="8" objectname="Test.dbo.testklup" indexname="inx_cls" id="locka49e800" mode="X" associatedObjectId="72057594040025088"> <owner-list> <owner id="process2dc2aa8" mode="X"/> </owner-list> <waiter-list> <waiter id="process2b6b000" mode="S" requestType="wait"/> </waiter-list> </keylock> <keylock hobtid="72057594040090624" dbid="8" objectname="Test.dbo.testklup" indexname="inx_nlcs" id="locka47ab40" mode="S" associatedObjectId="72057594040090624"> <owner-list> <owner id="process2b6b000" mode="S"/> </owner-list> <waiter-list> <waiter id="process2dc2aa8" mode="X" requestType="wait"/> </waiter-list> </keylock> </resource-list> </deadlock> </deadlock-list>View Code
SPID=57:查詢語句使用非聚集索引inx_nlcs查詢nlskey=1的記錄,持有inx_nlcs上的S鎖;由於索引未覆蓋列cont2,需通過鍵查詢得到cont2,於是申請inx_cls上的S鎖。
SPID=55:更新語句使用聚集索引inx_cls查詢clskey=1的記錄,持有inx_cls上的X鎖;由於非聚集索引inx_nlcs包含列cont1,在更新表中記錄後,還需更新非聚集索引中的數值,於是申請inx_ncls上的X鎖。
--hobtid SELECT OBJECT_NAME(p.object_id) AS TableName, i.name AS IndexName FROM sys.partitions AS p INNER JOIN sys.indexes AS i ON p.object_id = i.object_id AND p.index_id = i.index_id WHERE p.hobt_id = 72057594040090624 --associatedObjectId SELECT OBJECT_NAME(p.object_id) AS TableName, i.name AS IndexName FROM sys.partitions AS p INNER JOIN sys.indexes AS i ON p.object_id = i.object_id AND p.index_id = i.index_id WHERE p.partition_id = 72057594040025088 --case when type = 1|3 then container_id = sys.partitions.hobt_id --case when type = 2 then container_id = sys.partitions.partition_id SELECT * FROM sys.allocation_units WHERE container_id=72057594040090624 SELECT * FROM sys.allocation_units WHERE container_id=72057594040025088 SELECT %%lockres%% AS keyhashvalue,* FROM testklup WHERE %%lockres%% ='(8194443284a0)'View Code