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

基于其他字段的自增列触发器并发重复值问题咨询

高并发下该触发器会产生重复position值,事务隔离无法有效避免

问题原因

你的触发器逻辑存在并发漏洞:当多个事务同时插入相同templateName的记录时,它们会在各自的事务中执行SELECT MAX(position),此时由于事务未提交,彼此无法看到对方的position更新,因此会读取到同一个最大值,最终都将position设置为max+1,导致重复值出现。

默认的READ COMMITTED事务隔离级别只能保证读取已提交的数据,但无法解决这种"读取-修改-写入"(Read-Modify-Write)的并发冲突。即使提升到REPEATABLE READ或更高的隔离级别,也只能保证单个事务内的读取一致性,无法阻止多个独立事务读取到相同的初始最大值。SERIALIZABLE隔离级别虽然能避免,但会强制事务串行执行,严重降低并发性能,不适合高并发场景。

解决方案

推荐使用独立计数器表+原子更新的方式,确保获取下一个position的操作是原子性的,避免并发冲突:

  1. 先创建计数器表,存储每个templateName的当前最大position:
CREATE TABLE TemplatePositionCounter (
    templateName VARCHAR(100) PRIMARY KEY,
    currentMaxPosition SMALLINT DEFAULT 0
)
  1. 修改触发器逻辑,通过原子更新计数器来获取唯一的position值:
CREATE TRIGGER [dbo].[setPosition]
ON [dbo].[myTable]
AFTER INSERT
AS 
BEGIN
    DECLARE @newPosition SMALLINT
    DECLARE @templateName VARCHAR(100)
    DECLARE @id INT

    SELECT @id = id, @templateName = templateName FROM inserted

    -- 原子更新计数器,同时获取下一个position值
    UPDATE TemplatePositionCounter
    SET currentMaxPosition = currentMaxPosition + 1,
        @newPosition = currentMaxPosition + 1
    WHERE templateName = @templateName

    -- 处理新的templateName,初始化计数器并设置position为1
    IF @@ROWCOUNT = 0
    BEGIN
        SET @newPosition = 1
        INSERT INTO TemplatePositionCounter (templateName, currentMaxPosition)
        VALUES (@templateName, 1)
    END

    UPDATE [myTable]
    SET position = @newPosition WHERE id = @id  
END

额外保障

为了彻底防止重复值,建议给myTable添加唯一约束:

ALTER TABLE [dbo].[myTable]
ADD CONSTRAINT UQ_myTable_TemplateName_Position UNIQUE (templateName, position)

这样即使出现极端异常情况,数据库也会直接报错,避免脏数据入库。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.07 08:22:49