我们的应用程序中存在死锁情况的问题。最近几天,我读了很多有关阻塞,锁定和死锁的文章,以期了解有关该问题的知识以解决该问题。
现在,当我阅读有关死锁的错误日志信息时,我不明白这种情况如何存在。看一下(我已重命名了表名,但重要的是在日志消息中称为OurTable的表名):
deadlock-list
deadlock victim=process1e2ac02c8
process-list
process id=process1e2ac02c8 taskpriority=0 logused=0 waitresource=OBJECT: 11:290100074:0 waittime=704 ownerId=3144354890 transactionname=SELECT lasttranstarted=2011-12-01T14:43:20.577 XDES=0x80017920 lockMode=S schedulerid=6 kpid=7508 status=suspended spid=155 sbid=0 ecid=0 priority=0 trancount=0 lastbatchstarted=2011-12-01T14:43:20.577 lastbatchcompleted=2011-12-01T14:43:20.577 clientapp=.Net SqlClient Data Provider hostname=DE-1809 hostpid=5856 loginname=2Ezy isolationlevel=read committed (2) xactid=3144354890 currentdb=11 lockTimeout=4294967295 clientoption1=673185824 clientoption2=128056
executionStack
frame procname=.dbo.RetrieveSomething line=23 stmtstart=1398 stmtend=3724 sqlhandle=0x03000b0030d42d645a63e6006a9f00000100000000000000
select
Col1
,Col2
,(
SELECT TOP(1)
Col1
FROM
OurTable2 AS C
JOIN OurTable AS ETC ON C.Id = ETC.FKId
AND E.Id = C.FKId
ORDER BY ETC.Col2
) AS Col3
from OurTable3 AS E
process id=process2df4894c8 taskpriority=0 logused=0 waitresource=OBJECT: 11:290100074:0 waittime=9713 ownerId=3144330250 transactionname=INSERT EXEC lasttranstarted=2011-12-01T14:43:11.573 XDES=0x370764930 lockMode=S schedulerid=13 kpid=4408 status=suspended spid=153 sbid=0 ecid=0 priority=0 trancount=1 lastbatchstarted=2011-12-01T14:43:11.573 lastbatchcompleted=2011-12-01T14:43:11.573 clientapp=.Net SqlClient Data Provider hostname=DE-1809 hostpid=5856 loginname=2Ezy isolationlevel=read committed (2) xactid=3144330250 currentdb=11 lockTimeout=4294967295 clientoption1=673185824 clientoption2=128056
executionStack
frame procname=adhoc line=1 sqlhandle=0x02000000ba6cb42612240bdb19f7303e279a714276c04344
select
Col1
, Col2
, Col3
, ISNULL(
(select top(1)
E_SUB.Col1 + ' ' + E_SUB.Col2
from OurTable3 as E_SUB
inner join OurTable2 as C on E_SUB.Id = C.FKId
inner join OurTable as ETC on C.Id = ETC.FKId
as Col3
from OurTable4
inner join dbo.OurTable as ETC on Id = ETC.FKId
process id=process8674c8 taskpriority=0 logused=0 waitresource=OBJECT: 11:290100074:5 waittime=338 ownerId=3143936820 transactionname=INSERT lasttranstarted=2011-12-01T14:38:24.423 XDES=0x1ecd229f0 lockMode=X schedulerid=7 kpid=12092 status=suspended spid=124 sbid=0 ecid=0 priority=0 trancount=2 lastbatchstarted=2011-12-01T14:38:23.027 lastbatchcompleted=2011-12-01T14:38:23.013 clientapp=.Net SqlClient Data Provider hostname=DE-1809 hostpid=5856 loginname=2Ezy isolationlevel=read committed (2) xactid=3143936820 currentdb=11 lockTimeout=4294967295 clientoption1=673185824 clientoption2=128056
executionStack
frame procname=.dbo.UpsertSomething line=332 stmtstart=27712 stmtend=31692 sqlhandle=0x03000b00bbf2a93c0f63a700759f00000100000000000000
insert into dbo.OurTable
(
Col1
,Col2
,Col3
)
values
(
@Col1
,@Col2
,@Col3
)
resource-list
objectlock lockPartition=0 objid=290100074 subresource=FULL dbid=11 objectname=dbo.OurTable id=lock16a1fde80 mode=X associatedObjectId=290100074
owner-list
waiter-list
waiter id=process1e2ac02c8 mode=S requestType=wait
objectlock lockPartition=0 objid=290100074 subresource=FULL dbid=11 objectname=dbo.OurTable id=lock16a1fde80 mode=X associatedObjectId=290100074
owner-list
owner id=process8674c8 mode=X
waiter-list
waiter id=process2df4894c8 mode=S requestType=wait
objectlock lockPartition=5 objid=290100074 subresource=FULL dbid=11 objectname=dbo.OurTable id=lock212f0f300 mode=IS associatedObjectId=290100074
owner-list
owner id=process1e2ac02c8 mode=IS
waiter-list
waiter id=process8674c8 mode=X requestType=wait
我的阅读方式是:
spid 155正在等待OurTable上的共享表锁定
(spid 124拥有冲突的X锁)
spid 153正在等待OurTable上的共享表锁定
(spid 124拥有冲突的X锁)
spid 124正在等待OurTable上的独占表锁
(spid 155拥有一个冲突的IS锁)
我的问题是这怎么可能发生。两个 session 同时在整个表上持有一个锁。我认为通常的死锁是两个或两个以上的 session 在不同的资源上持有锁并互相等待。但是这里的锁在同一资源上。它不是对索引的锁定,而是对表的锁定。此错误在我们的应用程序中很常见,某些锁必须是要请求的第一个锁,如果整个表上已经有一个锁,为什么要接受第二个锁呢?
谁能提示可能是什么错误,或者有人遇到过类似的僵局?
最佳答案
经过更多的搜索和测试后,我非常有信心可以为自己的问题提供正确的答案。
我要感谢马丁·史密斯(Martin Smith),他指出等待资源是不同的,从而使我朝着正确的方向前进。
正如马丁在评论中所写,等待资源是:11:290100074:0和11:290100074:5。
搜索之后,结果表明,如果您在具有16个CPU或更多CPU的计算机上运行Sql Server R2,则Sql Server可以使用称为lock partitioning的功能。
这篇文章说:
在我的情况下,发生的情况是spid 155在行或页上放置了共享锁,因此在对象上放置了预期的共享锁,并且具有锁分区功能,它恰好位于分区ID 5上。
同时,spid 124需要使用排他锁来锁定整个对象,因此需要在所有分区上放置X锁。
当到达分区标识5时,将通知spid 155持有IS锁,它需要等待直到该锁被释放。
现在,当spid 124正在等待要释放的IS锁时,lock escalation出现在spid 155上,它请求在表上共享锁。这意味着它需要将S锁置于从id 0开始的所有分区上。但是,在id 0上它立即撞墙,因为spid 124已经在该分区上拥有排他锁。在那里,您有造成僵局的原因。
我不能保证100%这是确切的答案,但是我很确定我是正确的,即使不是100%正确,至少也很接近答案。
解决方案?出色地。锁分区功能无法关闭,但另一方面,您可以使用不同的事务级别以及alter table语句中的不同选项来控制lock escalation。
我将继续研究为什么查询强制锁定升级,因为我认为我的特定情况下的解决方案是以某种方式调整查询以使其不升级。至少我会在使用上述工具之前尝试一下。
希望这个答案可以帮助其他有类似问题的人。