SQL 更新导致死锁

Posted

技术标签:

【中文标题】SQL 更新导致死锁【英文标题】:SQL Update causing deadlock 【发布时间】:2012-05-29 07:21:10 【问题描述】:

我们最近将一些代码从 C# 移至 SQL (SQL Server 2005) 以尝试防止并发问题。然而,在 SQL 中,我们现在遇到了死锁。我无法重新创建导致死锁的步骤,但是我能够在 SQL 跟踪中捕获它。

表上没有触发器,但有几个索引支持搜索。

根据跟踪,死锁是由两个人在同一条记录上运行相同的更新语句引起的:

UPDATE myTable
SET
  col2 = @var2 + col2
  ,col3 = CASE WHEN (@Var2 <= 0 OR @Var2 + Col2 <= 0)
            THEN Col3
          ELSE
            CONVERT
            (
               dbo.MoneyInfo,
               @var3 + ':' + @Var4 + ':' + @Var5
            )
          END
OUTPUT INSERTED.Col0,
       Inserted.Col2,
       Inserted.Col3
WHERE Col0 = @Var1

dbo.MoneyInfo 是自定义 CLR 类型。该表看起来像:

create table myTable
(
    col0 int,
    col1 int,
    col2 decimal(18,2),
    col3 dbo.MoneyInfo
)

col0 为非聚集主键(PK_Stock),col1 为聚集索引(IX_Item)

这是跟踪中的死锁图:

我不明白运行完全相同的存储 proc 语句的 2 个人怎么会在同一个语句上陷入死锁。第一个连接不应该锁定记录,迫使第二个连接等待它可用吗?对于造成这种僵局的原因,我还有什么可以调查的吗?会不会是 OUTPUT 语句的原因?

【问题讨论】:

这是否在一个事务中,该事务中是否还有其他语句? 它在一个事务中。不过,此表上没有其他查询。 这个问题是否通过更改隔离级别得到解决?我遇到了类似的死锁场景。 它为我们解决了问题 【参考方案1】:

是的,有可能两个人执行相同的语句而不锁定第一次执行。我也生成了相同的场景。 SQL 脏读问题。 使用ISOLATION LEVEL READ COMMITTED 事务 提供了相同的解决方案。 它指定语句不能读取已被其他事务修改但未提交的数据。这可以防止脏读。当前事务中的各个语句之间的其他事务可以更改数据,从而导致不可重复读取或幻像数据。 有关详细信息,请参阅 Microsoft 知识库 [http://msdn.microsoft.com/en-us/library/ms173763.aspx]

总结:- 只需将您的查询放入 Transaction [prefer - ISOLATION LEVEL READ COMMITTED transactions]

【讨论】:

肯定有 2 个人在运行它,我有跟踪文件显示。我将尝试将此事务的隔离级别设置为 READ COMMITTED 并查看它是否有所作为。

以上是关于SQL 更新导致死锁的主要内容,如果未能解决你的问题,请参考以下文章

在 Firebird 脚本中创建表会导致“元数据更新失败”并出现死锁

多次插入和更新导致死锁 c#

当 where 子句列中没有聚集索引时,并行更新导致死锁

NSFetchedResultsController 在同一持久存储的后台更新时提供表视图导致死锁

在执行读取/更新的 MS SQL 存储过程上获取死锁(放置代码来处理死锁)

更新查询在同一张表上的 Sql 查询死锁