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

SQL Server存储过程并发触发快照隔离更新冲突,能否按FIFO排队执行?

报错原因

你遇到的错误是SQL Server快照隔离级别的正常机制:快照隔离属于乐观并发控制,事务启动时会读取数据的行版本,不会加锁,等到实际执行更新操作时,才会校验该行是否在事务启动后被其他事务修改过,如果已被修改就会抛出更新冲突错误。

可选择的解决方案

方案1:精准加行锁(最推荐,性能影响最小)

如果你的冲突仅出现在多个请求同时更新同一个@IdJournee对应的行的场景,不需要全局串行,仅需要在更新语句上加锁提示即可:

update Journee WITH (UPDLOCK, HOLDLOCK)
set UVGlobalJournee = (select 
                           case
                               when max(E.UVGlobale) is null
                                   then 0 
                                   else max(E.UVGlobale) 
                           end
                       from 
                           Examen E 
                       inner join 
                           IT_Journee_Examen IT on E.IdExamen = IT.IDEXAMEN 
                       where 
                           Journee.IdJournee = IT.IDJOURNEE) 
where Test = '1'  
  and Journee.IdJournee = @IdJournee
  • UPDLOCK提示会在读取行时就加上互斥的更新锁,第一个获取锁的事务执行完成之前,其他同IdJournee的更新请求会进入等待状态,不会触发快照冲突,不同IdJournee的更新请求互不影响,性能损失极小。

方案2:全局FIFO串行执行

如果确实需要所有存储过程调用严格按先进先出顺序执行,不管更新的IdJournee是否相同,可以使用SQL Server自带的应用程序锁实现全局排队:

CREATE PROCEDURE [你的存储过程名称]
@IdJournee INT -- 原有参数
AS
BEGIN
    SET NOCOUNT ON;
    -- 请求全局独占锁,锁名称自定义为全局唯一值即可,@LockTimeout=-1表示无限等待
    EXEC sp_getapplock 
        @Resource = 'Global_Lock_Journee_Update_Proc',
        @LockMode = 'Exclusive',
        @LockOwner = 'Transaction',
        @LockTimeout = -1;

    -- 原有更新逻辑
    update Journee 
    set UVGlobalJournee = (select 
                               case
                                   when max(E.UVGlobale) is null
                                       then 0 
                                       else max(E.UVGlobale) 
                               end
                           from 
                               Examen E 
                           inner join 
                               IT_Journee_Examen IT on E.IdExamen = IT.IDEXAMEN 
                           where 
                               Journee.IdJournee = IT.IDJOURNEE) 
    where Test = '1'  
      and Journee.IdJournee = @IdJournee;

    -- 显式释放锁,事务提交时也会自动释放
    EXEC sp_releaseapplock
        @Resource = 'Global_Lock_Journee_Update_Proc',
        @LockOwner = 'Transaction';
END

该方案可以实现严格的FIFO执行,缺点是所有请求串行执行,并发量较高时性能下降明显。

方案3:调整隔离级别或增加重试逻辑

  • 可以单独将该存储过程的事务隔离级别调整为默认的读提交,避免快照隔离的乐观冲突,读提交级别会自动等待行锁释放,不会抛出冲突错误:在存储过程开头添加SET TRANSACTION ISOLATION LEVEL READ COMMITTED;即可。
  • 如果冲突概率极低,也可以在应用层捕获错误码为3960的快照冲突异常,延迟1~3秒后重试存储过程调用即可。

内容的提问来源于stack exchange,提问作者Patrice Hinodev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.28 17:57:05