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

并行事务中两个并发INSERT ON CONFLICT UPDATE结果解析

问题前提

初始状态下,表some_table中不存在id=1的记录,两个事务均执行针对id=1的UPSERT操作,需要判断不同执行时序下的最终结果。

时序规则:横轴为时间从左到右推进,B标记事务启动点,连续+覆盖范围为事务执行操作的阶段,C标记事务提交点。


场景1:事务1先启动、先提交

执行时序:

(1) -------------B+++++++++++++++++++++++C------------------------->
(2) --------------------B+++++++++++++++++++++++C------------------>

两个事务的执行语句:

  • 事务1:
INSERT INTO some_table (id, amount) VALUES (1, 10)
ON CONFLICT DO
  UPDATE some_table SET amount=amount+10 WHERE id=1;
  • 事务2:
INSERT INTO some_table (id, amount) VALUES (1, 20)
ON CONFLICT DO
  UPDATE some_table SET amount=amount+20 WHERE id=1;

执行流程

  1. 事务1先启动执行UPSERT:通过当前快照读确认无id=1的已提交记录,随即插入id=1的唯一索引项,拿到该记录的排他行锁,写入行数据id=1, amount=10,未提交。
  2. 事务2启动执行UPSERT:快照读同样未发现id=1的已提交记录,但尝试插入索引项时检测到锁冲突,进入阻塞等待,直到事务1释放锁。
  3. 事务1完成提交,持久化id=1, amount=10的记录,释放锁。
  4. 事务2获取锁后重新校验唯一约束,发现id=1的记录已存在,触发冲突更新逻辑:将现有amount值加20,更新为30,随后提交。

最终结果

表中存在id=1的记录,amount值为30。

追问场景:事务1先启动、事务2先提交

执行时序:

(1) -------------B+++++++++++++++++++++++++++++++++++C------------->
(2) --------------------B+++++++++++++++++++++++C------------------>

执行流程

  1. 事务1先启动,执行UPSERT时先通过快照读确认无id=1的已提交记录,但还未完成索引插入、加锁的步骤。
  2. 事务2启动后执行UPSERT,同样通过快照读确认无id=1的已提交记录,且率先完成索引项插入,拿到排他行锁,写入行数据id=1, amount=20。
  3. 事务1尝试插入索引项时检测到锁冲突,进入阻塞等待。
  4. 事务2先完成提交,持久化id=1, amount=20的记录,释放锁。
  5. 事务1获取锁后重新校验唯一约束,发现id=1的记录已存在,触发冲突更新逻辑:将现有amount值加10,更新为30,随后提交。

最终结果

表中存在id=1的记录,amount值同样为30。

补充说明

上述流程基于数据库默认的读提交(Read Committed)隔离级别,全程不会触发死锁,两个事务的增量更新都不会丢失,结果一致。即便是使用可串行化(Serializable)隔离级别,该场景也不会抛出序列化冲突错误,最终结果不变。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 11:51:18