并行事务中两个并发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先启动执行UPSERT:通过当前快照读确认无id=1的已提交记录,随即插入id=1的唯一索引项,拿到该记录的排他行锁,写入行数据
id=1, amount=10,未提交。 - 事务2启动执行UPSERT:快照读同样未发现id=1的已提交记录,但尝试插入索引项时检测到锁冲突,进入阻塞等待,直到事务1释放锁。
- 事务1完成提交,持久化
id=1, amount=10的记录,释放锁。 - 事务2获取锁后重新校验唯一约束,发现id=1的记录已存在,触发冲突更新逻辑:将现有amount值加20,更新为30,随后提交。
最终结果
表中存在id=1的记录,amount值为30。
追问场景:事务1先启动、事务2先提交
执行时序:
(1) -------------B+++++++++++++++++++++++++++++++++++C-------------> (2) --------------------B+++++++++++++++++++++++C------------------>
执行流程
- 事务1先启动,执行UPSERT时先通过快照读确认无id=1的已提交记录,但还未完成索引插入、加锁的步骤。
- 事务2启动后执行UPSERT,同样通过快照读确认无id=1的已提交记录,且率先完成索引项插入,拿到排他行锁,写入行数据
id=1, amount=20。 - 事务1尝试插入索引项时检测到锁冲突,进入阻塞等待。
- 事务2先完成提交,持久化
id=1, amount=20的记录,释放锁。 - 事务1获取锁后重新校验唯一约束,发现id=1的记录已存在,触发冲突更新逻辑:将现有amount值加10,更新为30,随后提交。
最终结果
表中存在id=1的记录,amount值同样为30。
补充说明
上述流程基于数据库默认的读提交(Read Committed)隔离级别,全程不会触发死锁,两个事务的增量更新都不会丢失,结果一致。即便是使用可串行化(Serializable)隔离级别,该场景也不会抛出序列化冲突错误,最终结果不变。
内容的提问来源于stack exchange,提问作者Dmitry
相关产品推荐
相关产品推荐

