-
在MongoDB中,对单个文档的操作是原子操作。可以使用嵌入式文档和数组来在单个文档结构中维护数据之间的关系,而不是跨多个文档和集合,因此这种单文档原子性消除了许多实际用例对多文档事务的需求。
-
在事务中不允许影响数据库目录的操作,例如创建或删除集合或索引。例如,事务不能包含导致创建新集合的插入操作。
-
包含读取操作的多文档事务必须设置
read preference
参数为primary
。给定事务中的所有操作都必须路由到同一成员。
//开始会话
session = db.getMongo().startSession( { readPreference: { mode: "primary" } } );
coll1 = session.getDatabase("test").channel;
coll2 = session.getDatabase("test").order;
//开启事务
session.startTransaction({readConcern:{level:"local"},writeConcern:{w:"majority"}});
try {
coll1.insertOne({"name" :"test-2","age":NumberInt(20)})
coll2.insertOne( { "name" : "test","age":NumberInt(18) } );
} catch (error) {
//事务执行失败,回滚事务
session.abortTransaction();
throw error;
}
//提交事务
session.commitTransaction();
//释放当前事务会话
session.endSession();
- 当事务提交时,事务中所做的所有数据更改都将保存并在事务外部可见。也就是说,事务在回滚其他事务时不会提交一些更改。
- 在事务提交之前,事务中发生的数据更改在事务外部不可见。但是,当事务写入多个分片时,并非所有外部读取操作都需要等待提交事务的结果在分片中可见。例如,如果事务已提交且写入1在分片A上可见但写入2在分片B上尚未可见,则读取关注“本地”的外部读取可以读取写入1的结果而不会看到写入2。
- 当事务中止时,事务中所做的所有数据更改都将被丢弃而不会变得可见。例如,如果事务中的任何操作失败,则事务将中止,并且事务中所做的所有数据更改都将被丢弃而不会变得可见。
- 在4.0版中,MongoDB支持副本集上的多文档事务。
- 在4.2版本中,MongoDB引入了分布式事务,它增加了对分片集群上的多文档事务的支持,并整合了对副本集上的多文档事务的现有支持。
部署 | 最低限度featureCompatibilityVersion |
---|---|
副本集 | 4.0 |
分片 | 4.2 |
查看该参数:db.adminCommond({getParameter:1,featureCompatibilityVersion:1})
默认情况下,事务的运行时间必须小于一分钟。您可以修改此限制 transactionLifetimeLimitSeconds
的 mongod
实例。对于分片群集,必须为所有分片副本集成员修改参数。超过此限制的事务将被视为已过期,并将通过定期清理过程中止。
MongoDB根据需要创建尽可能多的oplog条目来封装事务中的所有写操作,而不是为事务中的所有写操作创建单个条目。这将删除单个oplog条目为其写入操作强加的16MB总大小限制。虽然删除了总大小限制,但每个oplog条目仍必须在16MB的BSON文档大小限制内。
如果事务包含任何写操作,则MongoDB 在提交时创建单个oplog(操作日志)条目。也就是说,事务中的各个操作没有相应的oplog条目。相反,单个oplog条目包含事务中的所有写操作。事务的oplog条目必须在16MB的BSON文档大小限制内。
transactionLifetimeLimitSeconds
该参数默认60秒。确保了过期的事务都会被定期中止,以减轻存储缓存压力。
如果任何事务操作从包含仲裁器的分片读取或写入,则其写入操作跨越多个分片的事务将发生错误并中止。
-
默认情况下,事务等待最多
5
毫秒来获取事务中操作所需的锁。如果事务无法在5
毫秒内获取其所需的锁,则事务将中止。 -
事务在中止或提交时释放所有锁。
-
maxTransactionLockRequestTimeoutMillis
参数调整事务等待获取锁的时间。
- 如果事务正在进行中并且事务外部的写入修改了事务中的操作稍后尝试修改的文档,则事务因写入冲突而中止。
- 如果事务正在进行并且已锁定要修改文档,则当事务外部的写入尝试修改同一文档时,写入将等待直到事务结束。
- 事务内部的读取操作可以返回过时数据。也就是说,事务内的读操作不能保证看到由其他已提交事务或非事务性写操作执行的写操作。
- 要避免单个文档的事务内部过时读取,可以使用该
db.collection.findOneAndUpdate()
方法。
session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } );
employeesCollection = session.getDatabase("hr").employees;
employeeDoc = employeesCollection.findOneAndUpdate(
{ _id: 1, employee: 1, status: "Active" },
{ $set: { employee: 1 } },
{ returnNewDocument: true }
);