【问题标题】:How do I avoid these deadlocks?如何避免这些死锁?
【发布时间】:2009-05-29 00:01:23
【问题描述】:

我有一个例程正在更新我的业务实体。更新涉及大约 6 个不同的表。所有命令都在一个事务中执行。

最近,我需要在从数据库访问查找表的例程中添加一些代码。查找代码已存在于另一个业务对象中,因此我使用了该业务对象。例如:

Using tr As DbTransaction = myConnection.BeginTransaction()
    ExecuteCommand1(tr)
    ExecuteCommand2(tr)
    If myLookupTable.GetLookupTable().FindById(id).HasFlagSet Then
        ExecuteCommand3(tr)
    End If
End Using

但是,查找表业务对象挂起/死锁。我认为这是因为它没有对原始例程使用的事务的引用。

在做了一些研究之后,我尝试将查找表逻辑放在它自己的事务中,将IsolationLevel 设置为ReadUncommitted。这给了我想要的结果。但是,经过进一步研究,我现在怀疑我是否正确实施了这一点。

假设对活动事务的引用对我的查找表对象不可用,我所描述的是否被认为是最佳实践?我觉得我可能错过了什么。

【问题讨论】:

  • command1 或 command2 是否对查找表进行了处理?
  • 不,他们没有。他们甚至不读它。但是,它们使用对查找表具有 FK 的表。这些都没有直观地向我暗示锁定问题。

标签: asp.net sql-server ado.net


【解决方案1】:

如果您在事务中间进行读取,那么您应该在事务上下文中进行,而不是使用不同的事务和脏读。幸运的是,有一个简单的解决方案:不使用 ADO.Net 事务对象,而是使用 .Net TransactionScope 对象。 ADO.Net 代码对它很敏感,并将在此事务中征集您的所有操作,包括您的其他业务组件读取。只需确保您的业务对象没有打开其他连接,这将导致尝试将现有事务升级为分布式事务并将新连接加入其中。

另一种方法是在每次调用时传递您的 SqlConnection/SqlTransaction 对,但这会在您的代码中到处传播丑陋的东西。

【讨论】:

    【解决方案2】:

    如果是我,我会重写逻辑,这样我就不必进行未提交的读取。

    【讨论】:

      【解决方案3】:

      避免死锁的黄金法则是在每个事务中始终以相同的顺序获取表锁。所以看看其他事务中的代码,看看他们采取表锁的顺序;然后确保您在交易中使用相同的订单。

      【讨论】:

        【解决方案4】:

        显然,您的查找正在尝试访问由事务 tr 独占锁定的行。如果您使用 readuncommitted 事务或在查找查询中使用 WITH(NOLOCK),您将看到可能正在发生并影响查找逻辑的事务的所有未提交更改。所以我不太确定这会有多可取。

        如果您需要在该事务期间进行查找,我认为最好找到一种方法来确保您的查找查询参与当前事务。如果所有这些操作都在同一个线程中执行,您可以做的一件事是在创建一个事务对象时将事务对象存储在线程本地存储中,并让 GetLookupTable 方法检查事务对象的线程本地存储,如果有是一个事务集,您可以从该事务对象获取连接。否则,您将创建一个新连接。这样,您的查找将成为该事务的一部分,并且它应该运行其逻辑而不会被当前事务阻塞,进而阻塞当前事务并因此导致死锁。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2013-06-07
          • 2012-08-14
          • 1970-01-01
          • 2015-04-14
          • 1970-01-01
          • 2017-12-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多