【问题标题】:Avoid Violation of PRIMARY KEY in delete\insert transaction?避免在删除\插入事务中违反主键?
【发布时间】:2021-02-19 17:43:11
【问题描述】:

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

DELETE FROM my_table
WHERE [id] = @Id

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

很多这样的查询在不同的线程中执行。 INSERT 和 DELETE 处于 transaction 中,类型为 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. 我们在 my_table 表中没有带有 @Id 的记录

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

问题是如何在Read Commited transaction type不使用MERGE语句的情况下避免这种情况?

【问题讨论】:

    标签: sql-server multithreading tsql concurrency transactions


    【解决方案1】:

    我们有两个具有相同@Id 的交易 我们在 my_table 表中没有带有 @Id 的记录

    这是范围锁定旨在解决的场景。在 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)
    

    【讨论】:

    • 大卫,看起来这个解决方案对我有用。你知道这会如何影响性能吗?它会显着降低性能吗?目前我每秒有超过 600 笔这样的交易。
    • 这是一个键范围锁,因此 ID 是一个键,它应该只在两个会话尝试插入相同的@ID(或者可能是两个连续的 ID)的情况下阻止。无论如何,对吞吐量的影响应该是最小的。
    猜你喜欢
    • 2023-03-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-15
    • 1970-01-01
    相关资源
    最近更新 更多