首页 > 解决方案 > UPDATE 会导致死锁吗

问题描述

同时运行相同的 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. 检测到死锁。

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

标签: sql-serversql-updatelockingdeadlockdatabase-deadlocks

解决方案


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

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

我这样设置你的 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 ;

结果如下:

蜘蛛 出价 对象标识 工业标识 类型 资源 模式 地位
58 11 0 0 D B 小号 授予
61 11 0 0 D B 小号 授予
61 11 565577053 0 标签 X 授予
58 11 565577053 0 标签 等待

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

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


推荐阅读