更新时死锁。事务级别设置为Read Committed
。在这种情况下如何避免僵局?
在其他情况下WITH (NOLOCK)
和WITH (UPDLOCK)
有帮助。
我得到了以下T-SQL
查询:
IF EXISTS (SELECT 1 FROM DEBTORS_CUSTOMERS WITH (NOLOCK) WHERE DebtorId = @DebtorId AND ClientFCCustomerNumber = @CustomerNumber)
UPDATE DEBTORS_CUSTOMERS WITH (UPDLOCK) SET StatusId = @StatusId WHERE DebtorId = @DebtorId AND ClientFCCustomerNumber = @CustomerNumber
ELSE
INSERT INTO DEBTORS_CUSTOMERS (DebtorId, ClientFCCustomerNumber, StatusId, DocId) SELECT @DebtorId, @CustomerNumber, @StatusId, @DocId
这里是我得到的死锁:
<resource-list>
<keylock hobtid="72057594105692160" dbid="63" objectname="EOTestDataGenerator.dbo.DEBTORS_CUSTOMERS" indexname="PK_DEBTORS_CUSTOMERS" id="lockdf8abb00" mode="X" associatedObjectId="72057594105692160">
<owner-list>
<owner id="process3f59048" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="processbdbfa088" mode="U" requestType="wait"/>
</waiter-list>
</keylock>
<keylock hobtid="72057594105692160" dbid="63" objectname="EOTestDataGenerator.dbo.DEBTORS_CUSTOMERS" indexname="PK_DEBTORS_CUSTOMERS" id="lockdf5ab200" mode="X" associatedObjectId="72057594105692160">
<owner-list>
<owner id="processbdbfa088" mode="X"/>
</owner-list>
<waiter-list>
<waiter id="process3f59048" mode="U" requestType="wait"/>
</waiter-list>
</keylock>
</resource-list>
每个事务处理多行,对吗?这应该不会为一行出现死锁
您可能会得到双插入,这是一个bug。两个会话可能得出没有行的结论,然后都将插入。
有两种方法可以使其安全:
- 发出select
WITH (ROWLOCK, UPDLOCK, HOLDLOCK)
,这是一个众所周知的锁提示序列。它需要一个锁来稳定您正在操作的数据。运行完这条语句后,您就拥有了自己的数据。然后可以插入或更新。您也可以将这三个语句合并到一个MERGE
中,但是仍然需要锁提示。此外,您还必须有某种全球性的秩序来发布写操作。现在不管你怎么锁,如果一个会话写a, B,而另一个会话按顺序B, a写,总是会出现死锁。获得全局顺序的一个简单方法是在单个MERGE
语句中发出所有写操作。查询处理器通常选择一个强制顺序的计划。 - 使用
SERIALIZABLE
隔离并在死锁时重试。