在Trace标志1222和TABLOCKX的帮助下,我有很好的处理和解决死锁的方法。我看到了一个新的僵局,我不明白为什么它会陷入僵局,以及如何解决它。我在SQL Server 2008 R2上,10.50.2861.0。
这个单一语句本身就是一个事务(使用我认识不需要的显式BEGIN / COMMIT语句)。根据死锁跟踪(标记1222),此语句在TableB
上锁定,试图锁定TableA
。 TableC无关紧要。
我原以为SQL Server不会开始处理这个语句,直到它可以在TableA
和TableB
上获得独占表锁。如果它这样做,我会期望它阻止做任何事情(被阻止),直到它可以在两个表上获得独占锁。相反,似乎SQL Server在它到达TableB
之前开始阅读TableA
(并将其锁定),然后当它到达TableA
时,它发现自己与另一个进程陷入僵局(一个不同的SQL语句),它在TableA
上有一个锁,而另一个进程正在尝试将数据插入TableB
。这个其他过程没有使用任何TABLOCKX。
我对此的解释是否正确?如何在运行此语句之前让SQL Server锁定两个表以避免死锁?
UPDATE a
SET StatusId = 9,
StatusLastUpdatedOn = GETDATE()
FROM dbo.TableA AS a WITH (TABLOCKX)
INNER JOIN dbo.TableC AS c ON c.StatusId = a.StatusId
WHERE c.IsComplete = 0
AND a.StatusId NOT IN (1, 3)
AND EXISTS
(
SELECT *
FROM dbo.TableB AS b WITH (TABLOCKX)
WHERE b.Value1 = a.Value1
AND b.ConditionA = 1
);
EDIT,根据@Bogdan Sahlean的请求,下面是TF1222输出 - 缩写的语句较长。上面的UPDATE语句是process4583288。它与process459d708陷入僵局。上面的UPDATE语句(process4583288)似乎是TableB
的所有者并等待访问TableA
。
12/11/2013 13:11:09,spid19s,Unknown,waiter id=process459d708 mode=IS requestType=wait
12/11/2013 13:11:09,spid19s,Unknown,waiter-list
12/11/2013 13:11:09,spid19s,Unknown,owner id=process4583288 mode=X
12/11/2013 13:11:09,spid19s,Unknown,owner-list
12/11/2013 13:11:09,spid19s,Unknown,objectlock lockPartition=0 objid=980471563 subresource=FULL dbid=11 objectname=dbname.dbo.TableB id=lock9e7bc880 mode=X associatedObjectId=980471563
12/11/2013 13:11:09,spid19s,Unknown,waiter id=process4583288 mode=X requestType=wait
12/11/2013 13:11:09,spid19s,Unknown,waiter-list
12/11/2013 13:11:09,spid19s,Unknown,owner id=process459d708 mode=IX
12/11/2013 13:11:09,spid19s,Unknown,owner-list
12/11/2013 13:11:09,spid19s,Unknown,objectlock lockPartition=0 objid=353147353 subresource=FULL dbid=11 objectname=dbname.dbo.TableA id=lock3f439dc80 mode=IX associatedObjectId=353147353
12/11/2013 13:11:09,spid19s,Unknown,resource-list
12/11/2013 13:11:09,spid19s,Unknown,Proc [Database Id = 11 Object Id = 1385795344]
12/11/2013 13:11:09,spid19s,Unknown,inputbuf
12/11/2013 13:11:09,spid19s,Unknown,EXEC dbo.usp_ProcedureB;
12/11/2013 13:11:09,spid19s,Unknown,frame procname=dbname.dbo.usp_ProcedureD line=127 stmtstart=6586 stmtend=7022 sqlhandle=0x03000b00108f9952edd5580183a200000100000000000000
12/11/2013 13:11:09,spid19s,Unknown,*** This is the INSERT statement that is deadlocking with the UPDATE statement posted in the Stackoverflow Question - shortened ***
12/11/2013 13:11:09,spid19s,Unknown,frame procname=dbname.dbo.usp_ProcedureB line=119 stmtstart=4972 stmtend=9598 sqlhandle=0x03000b00eb973e0a22e7ee007da200000100000000000000
12/11/2013 13:11:09,spid19s,Unknown,executionStack
12/11/2013 13:11:09,spid19s,Unknown,process id=process459d708 taskpriority=0 logused=236 waitresource=OBJECT: 11:980471563:0 waittime=2127 ownerId=23648431472 transactionname=user_transaction lasttranstarted=2013-12-11T13:11:07.233 XDES=0x27a62c3b0 lockMode=IS schedulerid=4 kpid=2788 status=suspended spid=61 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2013-12-11T13:11:07.233 lastbatchcompleted=2013-12-11T13:11:07.223 clientapp=.Net SqlClient Data Provider hostname=IP-0AF81DC9 hostpid=5388 loginname=some-user isolationlevel=read committed (2) xactid=23648431472 currentdb=11 lockTimeout=4294967295 clientoption1=673185824 clientoption2=128056
12/11/2013 13:11:09,spid19s,Unknown,Proc [Database Id = 11 Object Id = 200150858]
12/11/2013 13:11:09,spid19s,Unknown,inputbuf
12/11/2013 13:11:09,spid19s,Unknown,EXEC dbo.usp_ProcedureA;
12/11/2013 13:11:09,spid19s,Unknown,frame procname=dbname.dbo.usp_ProcedureC line=56 stmtstart=2452 stmtend=2616 sqlhandle=0x03000b004a0fee0be3f6ee007da200000100000000000000
12/11/2013 13:11:09,spid19s,Unknown,*** This is the UPDATE statement Statement Posted in the Stackoverflow Question - shortened ***
12/11/2013 13:11:09,spid19s,Unknown,frame procname=dbname.dbo.usp_ProcedureA line=148 stmtstart=7390 stmtend=8462 sqlhandle=0x03000b00806122731562150182a200000100000000000000
12/11/2013 13:11:09,spid19s,Unknown,executionStack
12/11/2013 13:11:09,spid19s,Unknown,process id=process4583288 taskpriority=0 logused=0 waitresource=OBJECT: 11:353147353:0 waittime=2170 ownerId=23648431512 transactionname=user_transaction lasttranstarted=2013-12-11T13:11:07.240 XDES=0x17d90d950 lockMode=X schedulerid=3 kpid=1164 status=suspended spid=66 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2013-12-11T13:11:07.030 lastbatchcompleted=2013-12-11T13:11:07.030 clientapp=.Net SqlClient Data Provider hostname=IP-0AF81DC9 hostpid=5388 loginname=some-user isolationlevel=read committed (2) xactid=23648431512 currentdb=11 lockTimeout=4294967295 clientoption1=673185824 clientoption2=128056
12/11/2013 13:11:09,spid19s,Unknown,process-list
12/11/2013 13:11:09,spid19s,Unknown,deadlock victim=process4583288
12/11/2013 13:11:09,spid19s,Unknown,deadlock-list
答案 0 :(得分:1)
SQL Server不会提前获取所有锁,即在提出查询计划之后和开始读取数据之前。它只是通过查询计划,按顺序执行其部分,并在需要时获取锁。在您的示例中,它必须首先执行内部查询,以便在以后尝试锁定TableB
之前锁定TableA
。
我不确定在这种情况下是否存在避免死锁的通用方法。您可以通过在交易顶部放置两个select top 1 * from ... WITH (TABLOCKX)
(每个表一个)来最小化其机会。由于它们会很快被执行,因此可能会有更少的机会进入。
答案 1 :(得分:1)
减少死锁机会的一种方法是确保查询运行得很快。
在更改sql server的行为之前(例如更改锁定模式级别等),我会确保死锁中涉及的查询得到很好的优化。