Joh*_*ohn 9 t-sql sql-server deadlock
我们的应用程序中存在死锁情况问题.在过去的几天里,我已经阅读了很多关于阻塞,锁定和死锁的内容,试图了解问题以便解决问题.
现在,当我读取有关死锁的错误日志信息时,我无法理解这种情况是如何存在的.看看这个(我已经重命名了表名,但重要的是在日志消息中名为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
Run Code Online (Sandbox Code Playgroud)
我读这个的方式是:
spid 155正在等待OurTable上的共享表锁(spid 124持有冲突的X锁)
spid 153正在等待OurTable上的共享表锁(spid 124保持冲突的X锁)
spid 124正在等待OurTable上的Exclusive表锁(spid 155保持冲突的IS锁)
我的问题是如何发生这种情况.两个会话同时在整个表上保持一个锁.我认为通常的死锁是两个或更多的会话锁定不同的资源并等待彼此.但是这里的锁是在同一个资源上.它不是对索引的锁定,而是在桌面上.这个错误在我们的应用程序中很常见,并且一些锁必须是第一个被请求的,如果整个表上已经锁定,为什么接受第二个锁?
任何可以暗示可能出错的人或任何经历过类似僵局的人?
经过更多的搜索和测试,我非常有信心我可以对自己的问题给出正确的答案。
我必须感谢 Martin Smith,他指出等待资源是不同的,让我找到了正确的方向。
正如 Martin 在他的评论中所写,等待资源是:11:290100074:0 和 11:290100074:5。搜索后发现,如果您在具有 16 个或更多 CPU 的机器上运行 Sql Server R2,Sql Server 能够使用称为锁分区的功能。
这篇文章除其他外说:
在单个分区上仅获取 NL、SCH-S、IS、IU 和 IX 锁定模式。
在我的情况下,spid 155 在行或页上放置了一个共享锁,因此在对象上放置了一个预期的共享锁,并且使用锁分区功能,这恰好在分区 ID 5 上。
同时 spid 124 需要使用排他锁锁定完整对象,因此需要在所有分区上放置 X 锁。
必须在从分区 ID 0 开始并按分区 ID 顺序之后的所有分区上获取 NL、SCH-S、IS、IU 和 IX 以外模式中的共享 (S)、排他 (X) 和其他锁。
当它到达分区 id 5 时,它被告知 spid 155 持有一个 IS 锁,它需要等待直到该锁被释放。
现在当 spid 124 正在等待 IS 锁被释放时锁升级会在 spid 155 上发生,并且它请求表上的共享锁。这意味着它需要在所有从 id 0 开始的分区上放置 S 锁。但在 id 0 上它立即撞墙,因为 spid 124 已经在该分区上持有独占锁。这就是导致僵局的原因。
我不能保证 100% 这是确切的答案,但我很确定我是,如果不是 100% 正确,至少接近答案。
解决方案?好。无法关闭锁分区功能,但另一方面,您可以使用不同的事务级别以及更改表语句中的不同选项来控制锁升级。
我将继续调查为什么查询会强制锁定升级,因为我相信在我的特定情况下的解决方案是以某种方式调整查询以不升级。至少我会在使用上述工具之前尝试一下。
希望这个答案能帮助其他有类似问题的人。