模擬死鎖及案例分析

草稿箱二十篇隨筆沒有發佈,零零散散記錄着曾經覺得還不錯的知識點。稍做整理髮布,方便之後查看。2015-11-26 18:04 整理,未發佈sql

一、模擬死鎖

首先建立測試數據,而後開啓必要的跟蹤,最後執行兩個語句模擬死鎖。app

1.一、建立測試數據

建立測試數據表、建立索引ide

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)

create 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'
View Code

在測試以前,先開啓監視死鎖的開關1222,讓SQL Server遇到死鎖時,在Errorlog文件裏打印出死鎖的詳細內容。測試

DBCC TRACEON(1222,-1)

也能夠同時使用SQL Trace來捕捉和死鎖相關的信息。主要選擇事件Locks->Daedlock graphspa

1.二、開啓死鎖會話

開啓一個會話進行修改code

----模擬高頻update操做
declare @i int
set @i = 100
while 1=1
begin
    update testklup set cont1 = @i where clskey = 1  
    set @i = @i+1
end
View Code

開啓另一個會話進行查詢orm

----模擬高頻select操做
declare @cont2 char(3000)
while 1=1
begin
    select @cont2=cont2 from testklup where nlskey=1
end
View Code

兩條語句一塊兒運行,無須多長時間就會有其中一個鏈接遇到死鎖的錯誤:
server

二、分析死鎖

在開啓必要的跟蹤並捕獲到死鎖後,就能夠對其進行分析。blog

2.一、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=wait
View 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.二、死鎖圖

在跟蹤文件裏,能夠看到這樣的死鎖圖形:

咱們能夠從跟蹤文件中,提取死鎖事件數據,將死鎖圖保存爲死鎖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

三、Deadlocks-Example

SQL Server上的一個奇怪的Deadlock及其分析方法:https://blogs.msdn.microsoft.com/apgcdsd/2012/02/27/sql-serverdeadlock/
SQL Server Deadlocks by Example:https://www.simple-talk.com/sql/performance/sql-server-deadlocks-by-example/

相關文章
相關標籤/搜索