【问题标题】:MSSQL Deadlock when update withc (updlock)使用 c 更新时 MSSQL 死锁(updlock)
【发布时间】:2015-05-26 13:30:25
【问题描述】:

我在更新时遇到了死锁。事务级别设置为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>

【问题讨论】:

  • 你为什么在这里使用NOLOCK?事实上,你为什么要使用它?通常应避免使用表格提示,但您在此处的代码是导致死锁的秘诀。您明确地忽略了表上的任何锁,然后尝试更新该行。这意味着如果其他东西在该行上锁定了您正在尝试更新它。删除这些表提示,您的查询将自行修复。 blogs.msdn.com/b/davidlean/archive/2009/04/06/…
  • 每个事务处理多行,对吧?这不应该为一行死锁(不过,您可能会遇到双重写入,这是一个错误)。
  • (NOLOCK) 更改为 (UPDLOCK, HOLDLOCK) 并从 UPDATE 语句中删除 UPDLOCK。然后将块包装在显式事务中。
  • @SeanLange 感谢您的提示,我在选择语句中为 UPDLOCK 更改了 NOLOCK,它似乎有效。我的应用程序出现了很多死锁问题。 NOLOCK 和 UPDLOCK 帮助解决了这些问题。如果没有表提示,我几乎每次都在更新数据库上的相同记录时遇到死锁。在我的情况下,双重写入不会是一个错误。
  • @DanGuzman 该语句已在交易中。但是事务是从TransactionScope 中的 C# 代码设置的,并且我将事务级别设置为 Read Committed。

标签: sql-server tsql sql-update database-deadlocks


【解决方案1】:

每个事务处理多行,对吗?这不应该为一行死锁

不过,您可能会得到双插入,这是一个错误。两个会话可能会得出没有行的结论,然后两个会话都会插入。

有两种方法可以确保安全:

  1. 发出选择WITH (ROWLOCK, UPDLOCK, HOLDLOCK),这是众所周知的锁定提示序列。它需要一个锁来稳定您正在操作的数据。运行此语句后,您将拥有自己的数据。然后您可以插入或更新。您也可以将所有三个语句合并为一个MERGE,但您仍然需要锁定提示。此外,您必须有某种全局顺序来发出写入。现在,无论您如何锁定,如果一个会话写入 A、B,而另一个会话按 B、A 顺序写入,则总是会出现死锁。获得全局顺序的一种简单方法是在单个 MERGE 语句中发出所有写入.查询处理器通常会选择一个强制执行顺序的计划。
  2. 使用SERIALIZABLE隔离并重试死锁。

【讨论】:

  • 当我使用第二种解决方案时,我再次陷入僵局。第一个似乎在此刻起作用。谢谢大家!
  • 第二种解决方案的要点是继续出现死锁但重试它们。这可能是一个快速有效的解决方案。
  • 但是不存在无限死锁的风险吗?会话 A 和 B 不会一次又一次地重试以共享相同的资源?
  • 对于每一个解决的死锁,只有一个会话存在并取得进展。进步是有保证的。
  • 我已经检查了第二个解决方案。似乎工作完美。并且存储过程的更改更少。您对将隔离级别设置为已提交读有何看法?不会比 Serializable 更快吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-21
  • 1970-01-01
  • 2017-10-18
  • 2017-03-31
  • 1970-01-01
  • 2021-04-23
相关资源
最近更新 更多