【问题标题】:Spring mongo thread safetySpring mongo 线程安全
【发布时间】:2020-06-17 15:29:24
【问题描述】:

我正在学习 Spring Boot 和并发。我知道当spring boot收到多个请求时,它会启动多个线程来处理请求。我这里有一个访问 mongo 的方法。此方法将保存一个新的 someResult(可能包含调用者设置的一些新值)。我的问题是,如果我的 spring boot 控制器有 100 个并发调用,并且我得到 someResult 对象,并设置值和保存等,这些值会不一致吗?

  public void upsert(SomeResult someResult) {
        String collection = this.SomeResultConfig.getCollectionSomeResultCollection();
        String queryStr = "{testingID : '%s'}";
        queryStr = String.format(queryStr, someResult.getTestingID());
        Query query = new BasicQuery(queryStr);
        List<SomeResult> someResultList = this.mongoOps.find(query, SomeResult.class, collection);
        if (someResult.size() != 0) {
            this.mongoOps.findAllAndRemove(query, collection);
        }
        this.mongoOps.save(someResult, collection);
    }

【问题讨论】:

  • Yes 值会不一致,因为您正在执行多个操作。使用事务。支持 Mongo server 4.x 版本和Spring data mongdb

标签: java spring-boot concurrency


【解决方案1】:

MongoDB 文档级别的操作是原子的。因此,如果多个线程修改同一个文档,则生成的文档将是一些这些更新操作的交错。如果这些更新操作更新公共值,则结果值将是上次更新时设置的值,这是不确定的。

但是,如果您的更新操作是多步骤操作(例如读取文档、在内存中对其进行修改并重新写回),那么当您编写文档时,无法保证您不会覆盖其他人所做的更改线。对于这些类型的更新,请考虑使用 mongo 事务,以便读取更新操作确保您更新的内容就是您读取的内容。

如果多个线程处理不同的文档,则不会有任何这些问题。

【讨论】:

    【解决方案2】:

    是的,有可能两个线程先读取结果,然后进行修改,然后每个线程将修改写入数据库。数据库状态以其中一个值结束 - 后一个值,较早的写入丢失 - 这称为 丢失更新 现象。

    除了使用具有足够隔离级别的事务之外,您还可以使用乐观锁定。

    @Version 注释在 MongoDB 上下文中提供类似于 JPA 的语法,并确保更新仅应用于具有匹配版本的文档。因此,版本属性的实际值以这样一种方式添加到更新查询中,即如果同时另一个操作更改了文档,则更新不会产生任何影响。在这种情况下,将引发 OptimisticLockingFailureException。以下示例显示了这些功能:

    https://docs.spring.io/spring-data/mongodb/docs/current/reference/html/#mongo-template.optimistic-locking

    在您的代码中,您可以决定是否要在抛出异常时重试整个操作,或者是否向用户报告错误。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-04-04
      • 1970-01-01
      相关资源
      最近更新 更多