避免在 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.
问题出现在:
- 我们有 2 笔交易具有相同的@Id
- 我们在 table my_table
中没有带@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)
我有非常简单的 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.
问题出现在:
- 我们有 2 笔交易具有相同的@Id
- 我们在 table my_table 中没有带@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)