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

并发执行SELECT与INSERT时是否会引发重复插入的竞态条件?

并发场景下非唯一索引字段的重复插入问题解答

结论

肯定会出现名称为“john”的数据被重复插入的情况。

原因

你这段代码是典型的「先查询后插入」逻辑,再加上name字段是非唯一索引,并发执行时完全会触发竞态条件:

  • 进程A查询数据库,没找到"john",准备执行插入
  • 进程B在进程A的插入操作还未提交到数据库前,也执行查询"john"的操作,同样返回空结果,进入插入分支
  • 最终两个进程都完成插入,数据库中会出现多条name为"john"的数据

另外,方法上的@Transactional无法阻止这种情况——默认的读已提交隔离级别仅能防止脏读,无法让进程B看到进程A未提交的插入操作,因此两个进程的查询结果都是空,都会执行插入逻辑。

解决办法

1. 给name添加唯一约束(最推荐)

直接在数据库的name字段上创建唯一索引,或者在实体类中通过@Column(unique = true)标注。这样即便多个进程同时执行插入,数据库会直接抛出唯一约束冲突的异常,从根源上杜绝重复数据。

2. 使用数据库锁拦截并发查询

在查询阶段就锁定相关数据,确保同一时间只有一个进程能获取查询结果:
比如用JPA的悲观锁注解改造查询方法:

@Lock(LockModeType.PESSIMISTIC_WRITE)
@Query("SELECT t FROM Test t WHERE t.name = :name")
Test findByNameForUpdate(@Param("name") String name);

进程A查询时会锁住对应数据,进程B必须等待A的事务提交后才能执行查询,自然不会触发重复插入。

3. 分布式锁(跨服务进程场景)

如果是多个独立的服务进程并发执行(非同一JVM内的线程),可以借助Redis或ZooKeeper实现分布式锁,保证同一时间只有一个进程能执行「查询-插入」的完整逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.22 07:36:19