死锁 - 在没有数据的情况下锁定列
Posted
技术标签:
【中文标题】死锁 - 在没有数据的情况下锁定列【英文标题】:Deadlock - Lock of a column whereas there is no data 【发布时间】:2013-10-17 13:54:25 【问题描述】:我在执行这个存储过程时遇到了死锁:
-- Delete transactions
delete from ADVICESEQUENCETRANSACTION
where ADVICESEQUENCETRANSACTION.id in (
select TR.id from ADVICESEQUENCETRANSACTION TR
inner join ACCOUNTDESCRIPTIONITEM IT on TR.ACCOUNTDESCRIPTIONITEMID = IT.id
inner join ACCOUNTDESCRIPTION ACC on IT.ACCOUNTDESCRIPTIONID = ACC.id
inner join RECOMMENDATIONDESCRIPTION RD on ACC.RECOMMENDATIONDESCRIPTIONID = RD.id
inner join RECOMMENDATION REC on REC.id = RD.RECOMMENDATIONID
inner join ADVICESEQUENCE ADV on ADV.id = REC.ADVICESEQUENCEID
where adv.Id = @AdviceSequenceId AND (@RecommendationState is NULL OR @RecommendationState=REC.[State])
);
这是表的架构:
这是死锁图:
you can see the detail of the deadlock graph here
所以,当我检索 ressource 节点的 associatedobjid 时,我确定它是主键和表 AdviceSequenceTransaction 的索引:
SELECT OBJECT_SCHEMA_NAME([object_id]), * ,
OBJECT_NAME([object_id])
FROM sys.partitions
WHERE partition_id = 72057595553120256 OR partition_id = 72057595553316864;
SELECT name FROM sys.indexes WHERE object_id = 31339176 and (index_id = 1 or index_id = 4)
PK_AdviceSequenceTransaction IX_ADVICESEQUENCEID_ADVICE
由于键 ParentTransactionId 和键 Primary 键上的 AdviceSequenceTransaction 表上存在关系,因此我在 ParentTransactionId 列上创建了一个索引。
而且我没有更多的死锁了。 但问题是我不知道为什么没有更多的死锁:-/
另外,在测试它的数据集上,ParentTransactionId 中没有数据。都是NULL。
那么,即使 ParentTransactionId 中没有数据(null),SQL Server 是否可以访问主键???
另一件事是我想在删除语句中删除一个联接:
delete from ADVICESEQUENCETRANSACTION
where ADVICESEQUENCETRANSACTION.id in (
select TR.id from ADVICESEQUENCETRANSACTION TR
inner join ACCOUNTDESCRIPTIONITEM IT on TR.ACCOUNTDESCRIPTIONITEMID = IT.id
inner join ACCOUNTDESCRIPTION ACC on IT.ACCOUNTDESCRIPTIONID = ACC.id
inner join RECOMMENDATIONDESCRIPTION RD on ACC.RECOMMENDATIONDESCRIPTIONID = RD.id
inner join RECOMMENDATION REC on REC.id = RD.RECOMMENDATIONID
inner join ADVICESEQUENCE ADV on ADV.id = REC.ADVICESEQUENCEID
where adv.Id = @AdviceSequenceId AND (@RecommendationState is NULL OR @RecommendationState=REC.[State])
);
进入:
delete from ADVICESEQUENCETRANSACTION
where ADVICESEQUENCETRANSACTION.id in (
select TR.id from ADVICESEQUENCETRANSACTION TR
inner join ACCOUNTDESCRIPTIONITEM IT on TR.ACCOUNTDESCRIPTIONITEMID = IT.id
inner join ACCOUNTDESCRIPTION ACC on IT.ACCOUNTDESCRIPTIONID = ACC.id
inner join RECOMMENDATIONDESCRIPTION RD on ACC.RECOMMENDATIONDESCRIPTIONID = RD.id
inner join RECOMMENDATION REC on REC.id = RD.RECOMMENDATIONID
where TR.AdviceSequenceId = @AdviceSequenceId AND (@RecommendationState is NULL OR @RecommendationState=REC.[State])
);
我删除了最后一个连接。但是如果我这样做,我又会陷入僵局!再说一次,我不知道为什么......
感谢您的启发:)
【问题讨论】:
【参考方案1】:在 WHERE 子句中使用复杂的复合连接通常会导致问题。 SQL Server 使用以下逻辑处理顺序处理子句(查看 here):
-
来自
开启
加入
在哪里
分组依据
使用多维数据集或使用汇总
有
选择
不同
订购人
顶部
使用视图或派生表或视图可大大减少获得所需结果所需的迭代 (TABLE) 扫描次数,因为查询中的重点更适合使用逻辑执行顺序。每个派生表(或视图)的 FROM 子句首先执行,限制传递给 ON 原因的结果集,然后是 JOIN 子句等等,因为您将参数传递给“内部 FROM”而不是“最外面的地方”。
所以你的代码可能看起来像这样:
delete from (SELECT ADVICESEQUENCETRANSACTION
FROM (SELECT tr.id
FROM ADVICESEQUENCETRANSACTION WITH NOLOCK
WHERE AdviceSequenceId = @AdviceSequenceId
)TR
INNER JOIN (SELECT [NEXT COLUMN]
FROM [NEXT TABLE] WITH NOLOCK
WHERE COLUMN = [PARAM]
)B
ON TR.COL = B.COL
)ALIAS
WHERE [COLUMN] = COL.PARAM
);
等等…… (我知道代码不能剪切和粘贴,但它应该可以传达总体思路)
通过这种方式,您首先将参数传递给“内部查询”,预加载有限的结果集(特别是如果您应该使用视图),然后再向外工作。在适当的地方使用锁定提示也将有助于防止您可能遇到的一些问题。这种技术还有助于使执行计划更有效地帮助您诊断块的来源,如果您还有任何块的话。
【讨论】:
【参考方案2】:如果您不介意脏读,一种方法是在表 ADVICESEQUENCETRANSACTION TR 上使用 WITH(nolock)。
【讨论】:
以上是关于死锁 - 在没有数据的情况下锁定列的主要内容,如果未能解决你的问题,请参考以下文章
在 SELECT ... INNER JOIN ... FOR UPDATE 的情况下,字符串的顺序是啥锁定以及如何避免死锁?