使用 Spring Data 动手实践 MongoDB 4.0 事务

工程 | Christoph Strobl | 2018年6月28日 | ...

随着 MongoDB 4.0 的发布,ACID 事务已经到达 Document 存储,强制执行要么全有要么全无的执行方式,并保持数据完整性。 因此,让我们直接通过观察同步和反应式执行模型来深入了解它。

在撰写本文时,MongoDB 多文档事务在单个副本集中受到支持,并且感觉就像您可能从关系数据库中熟悉的事务。 查看驱动程序 API,人们会立即感到宾至如归

try (ClientSession session = client.startSession()) {

    session.startTransaction();

    try {

        collection.insertOne(session, documentOne);
        collection.insertOne(session, documentTwo);

        session.commitTransaction();

    } catch (Exception e) {
        session.abortTransaction();
    }
}

逻辑会话通过帮助协调跨分布式节点的操作,为 MongoDB 的 因果一致性 以及事务奠定了基础。 可以从 client.startSession() 获得的客户端会话应该是短暂的,并且一旦不再需要就应该释放。 因此请确保 close() 它们。

在较低的协议级别,上面的代码片段转换为以下一系列命令,您可以在其中清楚地看到每个命令中都存在会话 (lsid)。 startTransaction 标志与第一个命令一起发送,表示事务开始。 完成后,通过发送 commitTransaction 来提交事务。

{ insert: "col", ordered: true, $db: "db",
  $clusterTime: { … },
  lsid: { id: { $binary: { base64 : "I3M7Nj…", … } } },
  txnNumber: 1,
  startTransaction: true,
  documents: [ { … } ] }

{ insert: "col", ordered: true, $db: "db",
  $clusterTime: { … },
  lsid: { id: { $binary: { base64 : "I3M7Nj…", … } } },
  txnNumber: 1,
  autocommit: false,
  documents: [ { …} ] }

{ commitTransaction: 1,
  $db: "admin",
  $clusterTime: { … },
  lsid: { id: { $binary: { base64 : "I3M7Nj…", … } } },
  txnNumber: 1 }

随着即将到来的 Spring Data Lovelace 版本的发布,MongoDB 模块将提供对同步和反应式事务的专用支持。

从同步部分开始,您可能已经熟悉 Spring Framework 的事务支持。 因此,存在 MongoTransactionManager 可能并不奇怪。 事务管理器本身是命令式世界中基于注解的事务支持的入口点。

现在,由于 MongoDB 在早期版本中不支持事务,因此您必须在 ApplicationContext 中显式注册 MongoTransactionManager。 当您这样做时,MongoTemplate 开始参与托管事务。 这是您需要记住的关键点。 以下示例显示了如何配置事务管理器

@Configuration
class Config extends AbstractMongoConfiguration {

  @Bean
  MongoTransactionManager transactionManager(MongoDbFactory dbFactory) {
    return new MongoTransactionManager(dbFactory);
  }
}


@Service
class DocumentService {

  private final MongoOperations operations;

  DocumentService(MongoOperations operations) {
    this.operations = operations;
  }

  @Transactional
  void insertDocuments() {

    operations.insert(documentOne);
    operations.insert(documentTwo);
  }
}

非常简单,不是吗? 嗯,有点。 但存在一些不明显的缺点。 预计 MongoDB 的下一个主要版本将支持 分片集群 环境,如果您尝试这样做,将会出错。 此外,作为 MongoDB 用户,您很可能已经习惯了它提供的所有便利。 其中一些功能在事务中不可用,包括几乎所有的元命令、创建集合、索引以及首次使用集合时的隐式集合创建。 为了避免错误和挫败感,请确保预先设置所需的结构。 此外,某些命令的行为可能略有不同。 例如,count 使用隐藏的集合统计信息,这些信息在事务中可能不准确。 该命令会出错,并且需要使用聚合计数文档。 可用的驱动程序已经通过提供利用聚合策略的替代 countDocuments 方法来解决此问题。

考虑到这一点,让我们继续进行反应式用法。

MongoDB ReactiveStreams 驱动程序 提供了多文档事务的反应式入口点。 将驱动程序原生的 Publisher 管道传输到 Reactor 类型使您可以表达事务性用法,如下所示

Mono.from(client.startSession()).flatMap(session -> {

  session.startTransaction();

  return Mono.from(collection.insertOne(session, documentOne))
    .then(Mono.from(collection.insertOne(session, documentTwo)))
    .onErrorResume(e -> Mono.from(session.abortTransaction())
      .then(Mono.error(e)))
    .flatMap(val -> Mono.from(session.commitTransaction())
      .then(Mono.just(val)))
    .doFinally(signal -> session.close());
});

我们需要确保事务终止,无论是成功还是回滚。 因此,onErrorResume(…) 确保事务在失败时回滚,并确保最终的 flatMap(…) 提交事务,这两者都保留了主流程结果或错误。 而且,与往常一样,请确保在不再需要时关闭会话(在 doFinally(…) 块中)。

与同步部分不同,在撰写本文时,没有可用的反应式事务管理器可以让您使用 @Transactional 注释方法,并让您继续完成简单地工作的事情。

相反,您可以通过 ReactiveMongoTemplate.inTransaction(…) 访问事务闭包。 它负责处理所有必需的会话、提交和中止,同时维护主流程结果。 回调中的处理步骤在 MongoDB 事务中执行,而外部的处理步骤不会影响事务。 这意味着闭包外部的处理错误不会导致事务中止,如下面的示例所示。

template.inTransaction().execute(action ->

    // All code in here runs inside the transaction
    action.insert(documentOne).then(action.insert(documentTwo)

  ).flatMap(val -> {
    // An exception here does not affect the transaction
  });

如果您需要在整个流程中访问 ClientSession,它在 Reactor Context 中可用,您可以从 ReactiveMongoContext.getSession() 获取它。

最后一件事:如果您尝试一下并向我们提供您的反馈,我们将非常高兴! 因此,请查看 Spring Data Examples,您可以在其中找到一个专用的 项目


如果您想了解更多关于 Spring Data 或整个 Spring 生态系统的信息,那么在华盛顿特区举行的即将到来的 SpringOne Platform 会议是最佳的时间和地点。 查看 会话 并注册!

获取 Spring 新闻通讯

通过 Spring 新闻通讯保持联系

订阅

抢占先机

VMware 提供培训和认证来加速您的进步。

了解更多

获得支持

Tanzu Spring 在一个简单的订阅中为 OpenJDK™、Spring 和 Apache Tomcat® 提供支持和二进制文件。

了解更多

即将举行的活动

查看 Spring 社区中所有即将举行的活动。

查看全部