You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SQL并发执行Insert-If-Not-Exists存储过程遇死锁,如何让进程等待而非成为受害者?

解决并发存储过程的死锁问题:让进程等待而非成为死锁受害者

嘿,这个问题我帮不少开发者踩过坑,咱们一步步来理清楚:

首先,这不是设计使然,是你的存储过程逻辑有缺陷

先看你的代码结构:

IF Exists () 
    Select 
Else 
    Set Transaction isolation level serializable 
    Begin Tran 
        If not exists() 
            Insert 
        Select 
    Commit Tran

这种“先在事务外检查Exists,再进入事务重复检查并插入”的模式,在高并发场景下天生容易触发死锁,原因是:

  1. 外层的Exists()查询会在Serializable隔离级别下加共享锁,多个进程都能通过这一步;
  2. 接着多个进程同时进入事务,尝试执行Insert操作,这时候需要把共享锁升级为排他锁;
  3. 每个进程都在等待其他进程释放共享锁,形成了循环等待——这就是死锁的核心原因,SQL Server会选择一个“受害者”终止进程来打破死锁。

当然可以让第二个进程等待,甚至避免死锁,这里给你几个可行方案:

方案1:用原子化的“检查+插入”语句,彻底避免死锁

把事务外的检查和事务内的操作合并成一个原子SQL语句,让SQL Server一次性处理检查和插入,这样不会出现中间的锁竞争:

SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
BEGIN TRY
    BEGIN TRANSACTION;

    -- 原子操作:检查不存在就插入,一步完成
    INSERT INTO YourTargetTable (Column1, Column2)
    SELECT @Param1, @Param2
    WHERE NOT EXISTS (
        SELECT 1 FROM YourTargetTable 
        WHERE Column1 = @Param1 -- 这里是你的唯一判断条件
    );

    -- 如果需要返回插入后的结果,可以在这里加Select
    SELECT * FROM YourTargetTable WHERE Column1 = @Param1;

    COMMIT TRANSACTION;
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
        ROLLBACK TRANSACTION;
    
    -- 捕获死锁错误(错误号1205),加入重试逻辑
    IF ERROR_NUMBER() = 1205
    BEGIN
        -- 可以设置重试次数,比如最多重试3次
        RAISERROR('死锁发生,正在重试...', 10, 1) WITH NOWAIT;
        WAITFOR DELAY '00:00:01'; -- 等待1秒后重试
        -- 这里可以通过循环或递归实现重试逻辑,根据你的业务需求调整
    END
    ELSE
    BEGIN
        -- 抛出其他错误,方便排查
        THROW;
    END
END CATCH

方案2:调整死锁优先级,降低成为受害者的概率

如果你的场景中必须保留原有逻辑,可以设置死锁优先级,让当前进程更不容易被选为受害者:

-- 在存储过程开头设置死锁优先级为高(默认是NORMAL)
SET DEADLOCK_PRIORITY HIGH;

不过这个方案只能降低概率,不能从根源避免死锁,所以更推荐方案1。

方案3:统一资源访问顺序

如果你的存储过程涉及多个表,确保所有进程都按照相同的顺序访问这些表(比如先访问表A,再访问表B),这样可以打破循环等待的条件,避免死锁。

总结

死锁不是必然的,是你的“先读后写”逻辑在并发下的产物。用原子化的SQL语句是最彻底的解决方式,配合TRY/CATCH的重试逻辑,就能让进程在遇到锁竞争时等待,而不是直接被终止。

内容的提问来源于stack exchange,提问作者j doe

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 08:43:01