使用 c (updlock) 更新时的 MSSQL 死锁
MSSQL Deadlock when update withc (updlock)
我在更新时遇到了死锁。事务级别设置为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>
您正在为每个事务处理多行,对吗?这不应该死锁一行
不过,您可能会得到双插入,这是一个错误。两个会话可能得出没有行的结论,然后都将插入。
有两种方法可以确保安全:
- 发出select
WITH (ROWLOCK, UPDLOCK, HOLDLOCK)
这是一个众所周知的锁提示序列。它需要一把锁来稳定您正在操作的数据。在这条语句有 运行 之后,您就拥有了自己的数据。然后您可以插入或更新。您也可以将所有三个语句合并为一个 MERGE
但您仍然需要锁提示。此外,您必须有某种全局顺序来发出写入。现在,无论您如何锁定,如果一个会话写入 A、B 而另一个会话按顺序写入 B、A,则始终会出现死锁。获得全局顺序的一种简单方法是在单个 [=11= 中发出所有写入] 陈述。查询处理器通常会选择一个执行顺序的计划。
- 使用
SERIALIZABLE
隔离并重试死锁。
我在更新时遇到了死锁。事务级别设置为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>
您正在为每个事务处理多行,对吗?这不应该死锁一行
不过,您可能会得到双插入,这是一个错误。两个会话可能得出没有行的结论,然后都将插入。
有两种方法可以确保安全:
- 发出select
WITH (ROWLOCK, UPDLOCK, HOLDLOCK)
这是一个众所周知的锁提示序列。它需要一把锁来稳定您正在操作的数据。在这条语句有 运行 之后,您就拥有了自己的数据。然后您可以插入或更新。您也可以将所有三个语句合并为一个MERGE
但您仍然需要锁提示。此外,您必须有某种全局顺序来发出写入。现在,无论您如何锁定,如果一个会话写入 A、B 而另一个会话按顺序写入 B、A,则始终会出现死锁。获得全局顺序的一种简单方法是在单个 [=11= 中发出所有写入] 陈述。查询处理器通常会选择一个执行顺序的计划。 - 使用
SERIALIZABLE
隔离并重试死锁。