分布式事务
单文档原子性可满足大多数业务需求
在 MongoDB 中,对单个文档的操作是原子操作。
由于 MongoDB 文档数据模型,一个文档中通过嵌入式的文档和数组来表示传统关系数据库模型中的一对一、一对多关系,而不是通过文档之间的复杂关系来描述业务需求中的一对一、一对多关系。
所以单文档原子性可以满足实际生产中大多数关于事务的需求。
对于需要对多个文档(在单个或多个集合中)进行原子读写的情况,MongoDB 支持多文档事务。使用分布式事务,可以跨多个操作,集合,数据库,文档和分片进行原子性操作。
Drivers API
为支持 MongoDB 事务,Drivers API 有两类:Callback API 和 Core API,前者更加自动化,后者需要手动去控制事务的开始,事务的提交等。
Callback API | Core API |
---|---|
|
|
驱动版本
要使用 MongoDB 4.2 版本的分布式事务,驱动必需满足如下版本:
编程语言 | 版本 |
---|---|
Node | 3.3.0 |
Python | 3.9.0 |
Go | 1.1 |
Java | 3.11.0 |
C# | 2.9.0 |
Ruby | 2.10.0 |
Callback API
String uri = "mongodb://localhost:27000,localhost:27001,localhost:27002/admin?replicaSet=repl";
final MongoClient client = MongoClients.create(uri);
/**
* Create collections. CRUD operations in transactions must be on existing collections.
*/
client.getDatabase("mydb1").getCollection("foo").withWriteConcern(MAJORITY).insertOne(new Document("abc", 0));
client.getDatabase("mydb2").getCollection("bar").withWriteConcern(MAJORITY).insertOne(new Document("xyz", 0));
final ClientSession clientSession = client.startSession();
TransactionOptions txnOptions = TransactionOptions.builder()
.readPreference(primary())
.readConcern(LOCAL)
.writeConcern(MAJORITY)
.build();
TransactionBody<?> txnBody = new TransactionBody<String>() {
@Override
public String execute() {
MongoCollection<Document> coll1 = client.getDatabase("mydb1").getCollection("foo");
MongoCollection<Document> coll2 = client.getDatabase("mydb2").getCollection("bar");
coll1.insertOne(clientSession, new Document("abc", 1));
coll2.insertOne(clientSession, new Document("xyz", 999));
return "SUCCESS";
}
};
try {
clientSession.withTransaction(txnBody, txnOptions);
} finally {
clientSession.close();
}
错误处理
通常数据库事务处理需要错误处理,以处理事务在开始、提交阶段可能遇到的异常,并对异常做相应处理,例如重新提交等。 MongoDB 提供了两个错误标签,分别具有不同的含义:
TransientTransactionError | UnknownTransactionCommitResult |
---|---|
在一个事物中的单个写操作是不可重试的(尽管配置了相应的 retryWrites),如果异常中的错误是这个标签,那么错误很可能是由于集群状态变化、主节点 step down 等造成,那么事物重试需要从重新开始提交开始。 | 通常可以将事物提交设定为可重试,这个标签显示事务提交结果不符合预期,如果错误处理中遇到的错误是这个标签,那么可以尝试重新提交事务 |
会话隔离
任意一个事务都绑定一个连接会话,开始一个事务是从连接会话对象 Session 开始的
任意一个时间点,一个连接会话对象 Session 只有一个运行中的事务
驱动中的事务操作 API 都必须管理一个连接会话对象 Session
如果连接会话对象 Session 结束,则关联的事务回取消,回滚相应的操作