PostgreSQL并行请求下Update+Insert事务能否正确执行?
结论:无法保证仅一次插入,并发场景下必然出现重复插入问题
原因分析
在READ COMMITTED隔离级别下,你的方案存在致命的并发漏洞:
- 当多个携带相同参数的请求同时触发,且数据库中还没有目标记录时:
- 请求A的事务执行
UPDATE,因无匹配行返回受影响行数0,准备执行INSERT; - 同时请求B的事务执行
UPDATE,由于A的事务尚未提交,B无法感知到A的未提交操作,同样返回受影响行数0,也进入INSERT流程; - 最终A、B先后提交事务,导致两条完全相同的记录被插入数据库。
- 请求A的事务执行
核心问题在于:UPDATE仅会对已存在的行加行级锁,当目标行不存在时,不会产生任何锁限制。多个并发事务的UPDATE会毫无阻碍地通过,进而触发重复插入。
可行的替代方案
既然无法使用唯一索引和INSERT ... ON CONFLICT DO UPDATE,可以通过以下方式规避并发问题:
1. 利用数据库专属锁机制
针对业务参数生成唯一标识,用数据库提供的锁函数串行化请求:
- MySQL:调用
GET_LOCK('业务参数唯一标识', 超时时间),获取锁成功后再执行后续逻辑,完成后调用RELEASE_LOCK释放锁; - PostgreSQL:调用
pg_advisory_lock(业务参数哈希值),将业务参数哈希为整数作为锁标识,执行完逻辑后调用pg_advisory_unlock释放。
2. 引入分布式锁
如果是分布式系统,可以用Redis、ZooKeeper等组件实现分布式锁,针对业务参数加锁,确保同一时间只有一个请求能进入UPDATE→INSERT的逻辑分支。
3. 谨慎提升事务隔离级别(仅辅助作用)
将隔离级别提升至REPEATABLE READ,能避免部分幻读场景,但依然无法解决“无匹配行时UPDATE不锁”的核心问题,不能彻底防止重复插入,仅能作为辅助优化手段。
内容的提问来源于stack exchange,提问作者Alena
相关产品推荐
相关产品推荐

