UPDATE 会导致死锁吗

时间:2021-02-11 04:33:48

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

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

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

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

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

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

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

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

1 个答案:

答案 0 :(得分:0)

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

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

我这样设置你的 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 个不同的会话(spids 61 和 58)运行相同的 UPDATE 查询,让事务挂起以查看使用了什么锁:

BEGIN TRAN ;

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

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

EXEC sp_lock 61, 58 ;

结果如下:

<头>
蜘蛛 出价 对象 ID IndId 类型 资源 模式 状态
58 11 0 0 数据库 S 赠款
61 11 0 0 数据库 S 赠款
61 11 565577053 0 标签 X 赠款
58 11 565577053 0 标签 IX 等待

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

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