抢占先机
VMware 提供培训和认证来加速您的进步。
了解更多随着 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 会议是最佳的时间和地点。 查看 会话 并注册!