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

每日单条数据UPSERT场景的并发安全实现方案咨询

每日单条数据UPSERT场景的并发安全实现方案咨询

你的信号量表锁行思路其实是可行的——相当于用一个全局的“每日资源锁”来串行化操作,确保同一时间只有一个会话能处理当天的数据,从根本上避免并发创建重复行的问题。不过确实有更简洁、更贴合数据库原生能力的方案可以用,不用额外维护一张锁表:

数据库原生UPSERT原子操作(推荐)

几乎所有主流关系型数据库都提供了原生的UPSERT(插入或更新)原子操作,这些操作由数据库内核保证并发安全,不用自己手动处理锁:

  • PostgreSQL:利用ON CONFLICT语法,前提是给day字段加唯一约束:
    INSERT INTO your_business_table (day, col1, col2)
    VALUES (CURRENT_DATE, 'val1', 'val2')
    ON CONFLICT (day) DO UPDATE
    SET col1 = EXCLUDED.col1, col2 = EXCLUDED.col2;
    
    当多个会话并发执行时,第一个会话会成功插入,后续会话会触发更新逻辑,全程是原子性的,不会出现重复行。
  • MySQL:用ON DUPLICATE KEY UPDATE,同样需要给day加唯一索引:
    INSERT INTO your_business_table (day, col1, col2)
    VALUES (CURDATE(), 'val1', 'val2')
    ON DUPLICATE KEY UPDATE col1 = VALUES(col1), col2 = VALUES(col2);
    
  • SQL Server:使用MERGE语句实现原子UPSERT:
    MERGE INTO your_business_table t
    USING (SELECT CAST(GETDATE() AS DATE) AS day) s
    ON t.day = s.day
    WHEN NOT MATCHED THEN
        INSERT (day, col1, col2) VALUES (s.day, 'val1', 'val2')
    WHEN MATCHED THEN
        UPDATE SET col1 = 'val1', col2 = 'val2';
    

这些原生方案的优势在于:不需要额外维护锁表,操作更简洁,而且数据库内核会自动处理并发冲突,比应用层手动加锁更高效可靠。

关于你提到的SELECT ... FOR UPDATE无效的原因

你说得没错,当目标行不存在时,SELECT ... WHERE day = today FOR UPDATE不会持有任何锁,所以第二个会话同样会查到0行,进而和第一个会话一起执行插入,最终导致重复行——这也是为什么原生UPSERT方案更可靠的原因,它在数据库层面解决了“不存在时插入、存在时更新”的原子性问题。

对你的信号量表方案的评价

这个方案是有效的“应用层锁”替代方案,适合那些无法使用原生UPSERT的场景(比如某些老旧数据库不支持)。但要注意两个点:

  • 必须确保resource = 'daily'的行始终存在,否则锁逻辑会失效;
  • 每次操作都要先加锁,多了一步额外的数据库交互,相比原生方案效率稍低。

备注:内容来源于stack exchange,提问作者Friedrich Roell

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.20 06:14:37