【问题标题】:Multithreading - Duplicate key exception during insert多线程 - 插入期间重复键异常
【发布时间】:2014-02-20 20:16:25
【问题描述】:

当我尝试在多线程环境中创建数据时遇到问题。在调用 super.create() 之前,我检查数据库中是否不存在数据。但我相信线程同时调用方法 create() 并且其中一个线程失败抛出重复键异常而不是 IllegalArgumentException :

    @Transactional(readOnly=false)
    @Override
    public T create(T resource){

      //check if data already exists
      if(this.repository.findById(resource.getId()) != null){
         throw new IllegalArgumentException("Resource already exists");
      }

      //else call create
      return super.create(resource);
    }

有什么办法让它起作用吗?该项目使用 spring-data-jpa 和 Hibernate 4.3.1.Final 和 Oracle 数据库

PS:我需要一个比只捕获异常更智能的解决方案,因为我在更新过程中遇到了同样的问题,如果资源日期比当前的更新日期,我必须检查集成日期是否更新资源数据库。

而且我的项目必须在多个节点上运行。

谢谢

【问题讨论】:

  • 你使用什么 id 生成策略?
  • 嗨,我正在使用序列生成器策略,但重复键实际上不在 id 字段上,上面的代码不完全是我正在使用的代码。实际上,检查是在另一个名为 applicationId (findByApplicationId) 的字段上进行的,它有一个唯一的约束。
  • 其他字段的值是否来自用户输入?还是它也生成了?另一个与您目前得到的答案更相关的问题:您是否在集群中运行?如果您有多个节点线程同步将无济于事。
  • 价值来自用户输入,是的,我需要运行我的项目的多个实例...
  • 通常情况下,可以在集群环境或这种情况下使用身份生成策略。 DB 会自动为你增加 id。

标签: java multithreading hibernate spring-data


【解决方案1】:

您可以使用带有同步的双重检查锁定 (http://en.wikipedia.org/wiki/Double-checked_locking) 来强化您的方法,以确保只有一个创建的对象,而其他线程只是跳过创建。诀窍是检查对象是否存在,然后进入同步块,然后再次检查。

伪代码:

if (noObjectExists()) {
    synchronized {
        if (noObjectExists()) {
            // Create object
        }
    }
}

【讨论】:

  • 谢谢,但是性能呢?我所有的服务都使用这个通用方法(创建)
  • 完整性与性能 ;-) 严重的是,如果创建不是长时间运行的任务,则在第一个线程创建实体后,后续线程只会检查是否存在并立即返回。
【解决方案2】:

如果您确定单个resource.id 只有一个resource,您可以锁定resource 对象。

@Transactional(readOnly=false)
@Override
public T create(T resource){
    synchronized (resource) {
        //check if data already exists
        if(this.repository.findById(resource.getId()) != null){
            throw new IllegalArgumentException("Resource already exists");
        }

        //else call create
        return super.create(resource);
    }
}

【讨论】:

  • 谢谢,我明天试试,我想这个解决方案,但我担心性能
【解决方案3】:

插入

  • 你可以使用 AtomicInteger 作为唯一键

更新

  • 多线程更新是一项痛苦的工作。您可以使用同步块。但这也是性能开销之一。更新等待不必要的行,即使它与此无关。所以我们可以创建一个针对唯一列的锁。所以同一行不会更新两次。同样在 Hibernate 中,我们可以在同步块中获取对象和更新。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-07-13
    • 2019-08-24
    • 1970-01-01
    • 2013-04-14
    • 1970-01-01
    • 2011-12-16
    • 2015-12-12
    • 2019-03-12
    相关资源
    最近更新 更多