避免在 delete\insert 事务中违反 PRIMARY KEY?

Avoid Violation of PRIMARY KEY in delete\insert transaction?

我有非常简单的 T-SQL 查询

DELETE FROM my_table
WHERE [id] = @Id

INSERT INTO my_table
    ([id], [payload])
VALUES
    (@Id, @Payload)

很多这样的查询在不同的线程中执行。 INSERT 和 DELETE 在 事务 中,类型为 Read Commited。 99.9% 的时间一切正常,但有一个边缘情况,当它失败时:

Violation of PRIMARY KEY constraint 'PK_my_table'. Cannot insert duplicate key in object 'dbo.my_table'. The duplicate key value is (9).
The statement has been terminated.

问题出现在:

  1. 我们有 2 笔交易具有相同的@Id
  2. 我们在 table my_table
  3. 中没有带@Id 的记录

因此第一个和第二个事务以 DELETE 开头。两个事务都没有删除任何内容,因为 table my_table 中没有记录。两者都开始 INSERT 和 BOOM - 违反 PRIMARY KEY。

问题是如何在不使用 MERGE 语句的情况下使用 Read Commited 事务类型避免这种情况?

We have 2 transactions with the same @Id We don't have a record with @Id in the table my_table

这是范围锁定旨在解决的场景。在 SERIALIZABLE 或 HOLDLOCK 提示下,您将对空键范围进行键范围锁定。

例如

drop table if exists tt
go
create table tt(id int primary key, a int)

begin transaction
    delete from tt 
    where id = 1
    select resource_type, request_mode, request_status
    from sys.dm_tran_locks
    where request_session_id = @@spid 
commit transaction

go

begin transaction
    delete from tt with (holdlock) 
    where id = 1
    select resource_type, request_mode, request_status
    from sys.dm_tran_locks
    where request_session_id = @@spid 
commit transaction

产出

(0 rows affected)
resource_type                                                request_mode                                                 request_status
------------------------------------------------------------ ------------------------------------------------------------ ------------------------------------------------------------
OBJECT                                                       IX                                                           GRANT

(1 row affected)


(0 rows affected)
resource_type                                                request_mode                                                 request_status
------------------------------------------------------------ ------------------------------------------------------------ ------------------------------------------------------------
KEY                                                          RangeX-X                                                     GRANT
OBJECT                                                       IX                                                           GRANT

(2 rows affected)