【问题标题】:Can UPDATE lead to deadlocksUPDATE 会导致死锁吗
【发布时间】:2021-02-11 04:33:48
【问题描述】:

同时运行相同的 UPDATE 查询会导致死锁吗?

假设有一个包含数百万条记录的表,我们需要一次更新数千条记录。

UPDATE TABLEX
SET Column1 = '1'
WHERE Column2 BETWEEN 1 AND 10000

我想知道并发运行此查询是否会导致死锁,因为每个更新查询可能会以不同的顺序获取页/行 U 锁。

换句话说,以下是一种可能的情况。场景是两个并发会话运行相同的查询来更新同一组记录。

  1. 会话 1:获取第 1 行的更新锁定(列 2 = 1)
  2. 会话 2:获取第 2 行的更新锁定(列 2 = 2)
  3. 会话 1:尝试获取第 2 行的更新锁,但失败,因为它已被会话 2 持有。(列 2 = 2)
  4. 会话 2:尝试获取第 1 行的更新锁,但失败,因为它已被会话 1 持有。(列 2 = 1)
  5. 检测到死锁。

在这里,我的假设是每个查询可能以不同的顺序扫描行,这意味着对行的锁定将以不同的顺序进行。

【问题讨论】:

  • 只有当 2 个进程试图以相反的顺序获取 2 个锁时才会发生死锁。直接更新不会发生这种情况,因为它们以相同的顺序获取相同的锁。你为什么要同时运行 same 更新? PS 反引号是怎么回事 - 这不是有效的 SQL Server 吗?
  • 好吧,如果您的查询可能相同?但是你为什么要运行 2 个相同的查询呢?从您的问题开始,您似乎正在同时更新 不同的 行?但后来你暗示相同的行 - 你能解释一下吗?
  • 对于您提出的问题,这可能过于简单化了。在这种情况下,细节有很大的影响。例如,我无法想象您会在您的网络应用程序上意外地同时更新来自 2 个不同客户端的 1000 行。
  • @DaleK 行锁是每行的,例如,如果查询 1 按分配顺序访问聚集索引的 2 行,而查询 2 按键顺序访问它们,那么两个行锁之间可能会出现死锁。诚然,这不太可能发生。当您的 NCI 不涵盖查询并导致乱序键查找时,死锁的可能性更大
  • @DaleK 它可能会死锁,是的。但我同意你刚刚提出的所有其他观点。我有一个纯理论的、人为的论点,这不太可能发生。

标签: sql-server sql-update locking deadlock database-deadlocks


【解决方案1】:

我同意其他人所说的——从两个不同的会话运行此特定查询不会导致死锁,因为不涉及交叉引用。

那么让我们看看当我们尝试时会发生什么......

我这样设置你的 TABLEX:

CREATE TABLE dbo.TableX
(
    [id] INT NOT NULL IDENTITY (1,1)
        CONSTRAINT [PK_TableX] PRIMARY KEY CLUSTERED
    , [Column1] CHAR(1) NULL
    , [Column2] INT NULL
        CONSTRAINT [UQ_TableX_Column2] UNIQUE
) ;
WITH cte_Nums AS
(
    SELECT  1 AS [n]
    UNION ALL
    SELECT  [n] + 1
    FROM    cte_Nums
    WHERE   [n] < 500000
)
INSERT
    INTO    dbo.TableX ( [Column1], [Column2] )
SELECT      'x', [n]
FROM        cte_Nums
OPTION      ( MAXRECURSION 0 ) ;
GO

然后我从 2 个不同的会话(spid 61 和 58)运行相同的 UPDATE 查询,让事务挂起以查看使用了哪些锁:

BEGIN TRAN ;

UPDATE  dbo.TableX
SET     [Column1] = '1'
WHERE   [Column2] BETWEEN 1 AND 10000 ;

然后,在第三次会话中,我运行sp_lock 来获取锁定信息:

EXEC sp_lock 61, 58 ;

结果如下:

spid dbid ObjId IndId Type Resource Mode Status
58 11 0 0 DB S GRANT
61 11 0 0 DB S GRANT
61 11 565577053 0 TAB X GRANT
58 11 565577053 0 TAB IX WAIT

因此,无论如何,在这种情况下,SQL Server 将整个表的排他 (X) 锁授予第一个查询 (spid 61),从而防止第二个查询 (spid 58) 接触任何行直到第一次更新完成。

根据您提供的示例,您的问题的答案是否定的。同样的 UPDATE 查询,从不同的会话并发运行,不会导致死锁,因为要更新的锁是在整个表上获取的,而不是在单个行上。

【讨论】:

  • 抱歉,这并不总是正确的。例如,如果正在运行的两个查询(或任何其他重新编译)之间的统计信息发生变化,它们可以使用不同的访问模式。这个特定示例可能会使用唯一索引中的键查找,但如果估计有很多行,它同样可以使用聚集索引扫描
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多