如何防止并发 T-SQL 事务中的死锁?

How to prevent deadlock in concurrent T-SQL transactions?

我有一个插入数百条记录的查询。查询背后的想法是:

  1. 删除旧记录id
  2. 插入具有相同 id
  3. 的新记录
  4. 如果 id 的记录不存在,将生成 eternal_id 的值
  5. 如果id的记录存在,我们应该保存eternal_id
  6. 的值
  7. Read Committed 类型的事务中执行的查询

查询如下:

DECLARE @id1 int = 100
DECLARE @id2 int = 200

CREATE TABLE #t(
    [eternal_id] [uniqueidentifier] NULL,
    [id] [int] NOT NULL
)

DELETE FROM [dbo].[SomeTable] WITH (HOLDLOCK)
        OUTPUT
             DELETED.eternal_id
            ,DELETED.id
        INTO #t
WHERE [id] IN (@id1, @id2)

INSERT INTO [dbo].[SomeTable]
            ([id]
            ,[title]
            ,[eternal_id])
SELECT main.*, ISNULL([eternal_id], NEWID())
FROM
(

    SELECT
     @id1 Id
    ,'Some title 1' Title

    UNION

    SELECT
     @id2 Id
    ,'Some title 2' Title

) AS main
LEFT JOIN #t t ON main.[id] = t.[id]

DROP TABLE #t

我有数百个线程使用不同的 @id 执行此查询。当 记录已经存在 [dbo].[SomeTable] 时一切正常,但是当 @id 的记录不存在时我正在捕捉:

Transaction (Process ID 73) was deadlocked on lock resources with another process and has been chosen as the deadlock victim. Rerun the transaction.

所以当2个或多个并发线程通过相同的@id[dbo].[SomeTable]中不存在的记录时,就会出现问题。 我试图在此处删除 WITH (HOLDLOCK)

DELETE FROM [dbo].[SomeTable] WITH (HOLDLOCK)
        OUTPUT
             DELETED.eternal_id
            ,DELETED.id
        INTO #t
WHERE [id] IN (@id1, @id2)

这还不够,我开始赶上了:

Violation of PRIMARY KEY constraint 'PK__SomeTable__3213E83F5D97F3D0'. Cannot insert duplicate key in object 'dbo.SomeTable'. The duplicate key value is (49). The statement has been terminated.

所以没有 WITH (HOLDLOCK) 即使记录已经存在,它也无法正常工作。

table 中不存在带有 id 的记录时如何防止死锁?

eternal_id 的条件更新可以这样完成:

update t set
    ...
    eternal_id = ISNULL(t.eternal_id, NEWID())
from [dbo].[SomeTable] t
where t.id = @id

因此,如果旧值存在,您将保留它。不需要delete/insert。除非你在触发器方面有一些魔力。

我认为@DaleK 上面的评论对我帮助最大。我会引用它:

While its a great ambition to try and avoid all deadlocks... its not always possible... and you can't prevent all future deadlocks from happens, because as more rows are added to tables query plans change. Any application code should have some form of retry mechanism to handle this. – Dale K

所以我决定实施某种形式的重试机制来处理这个问题。