Session.startTransaction()
On this page本页内容
Definition定义
Session.startTransaction(<options>)-
Starts a multi-document transaction associated with the session.启动与会话关联的多文档事务。At any given time, you can have at most one open transaction for a session.在任何给定的时间,会话最多可以有一个打开的事务。Changed in version 4.2.4.2版更改。Starting in MongoDB 4.2, multi-document transactions are available for both sharded clusters and replica sets.从MongoDB 4.2开始,多文档事务可用于分片集群和副本集。ImportantWithin a transaction, you can only specify read and write (CRUD) operations on existing collections.在事务中,只能对现有集合指定读写(CRUD)操作。For example, a multi-document transaction cannot include an insert operation that would result in the creation of a new collection.例如,多文档事务不能包含会导致创建新集合的插入操作。TheSession.startTransaction()method can take a document with the following options:Session.startTransaction()方法可以使用以下选项获取文档:{ readConcern: { level: <level>}, writeConcern: { w: <value>, j: <boolean>, wtimeout: <number> } }Option选项Description描述readConcernOptional.可选的。A document that specifies the read concern for all operations in the transaction, overriding operation-specific read concern.为事务中的所有操作指定读取关注的文档,覆盖特定于操作的读取关注。
You can specify one of the following read concern levels:您可以指定以下读取关注级别之一:For对于"local"and"majority"read concern, MongoDB may sometimes substitute a stronger read concern."local"和"majority"读取关注,MongoDB有时可能会取代更强的读取关注。writeConcernOptional.可选的。A document that specifies the write concern for the transaction.指定事务的写入关注的文档。This write concern applies to the transaction commit and abort operations.此写入关注适用于事务提交和中止操作。
The operations within the transaction use事务中的操作使用"w: 1", overriding operation-specific write concern."w: 1",覆盖特定于操作的写入关注。
If you commit using如果您使用"w: 1"write concern, your transaction can be rolled back during the failover process."w: 1"写入关注进行提交,那么您的事务可以在故障转移过程中回滚。
For MongoDB Drivers, transactions use the client-level write concern as the default.对于MongoDB驱动程序,事务默认使用客户端级别的写入关注。
Behavior行为
Operations Supported within a Transaction事务中支持的操作
If running with access control, you must have privileges for the operations in the transaction.如果使用访问控制运行,则必须对事务中的操作具有权限。
For multi-document transactions:对于多文档事务:
You can specify read/write (CRUD) operations on existing collections.您可以在现有集合上指定读/写(CRUD)操作。For a list of CRUD operations, see CRUD Operations.有关CRUD操作的列表,请参阅CRUD操作。Starting in MongoDB 4.4, you can create collections and indexes in transactions.从MongoDB 4.4开始,您可以在事务中创建集合和索引。For details, see Create Collections and Indexes In a Transaction有关详细信息,请参阅在事务中创建集合和索引The collections used in a transaction can be in different databases.事务中使用的集合可以在不同的数据库中。NoteYou cannot create new collections in cross-shard write transactions.不能在跨分片写入事务中创建新集合。For example, if you write to an existing collection in one shard and implicitly create a collection in a different shard, MongoDB cannot perform both operations in the same transaction.例如,如果您在一个分片中写入一个现有集合,并在另一个分片中隐式创建一个集合,则MongoDB无法在同一事务中执行这两个操作。You cannot write to capped collections. (Starting in MongoDB 4.2)您不能写入封顶集合。(从MongoDB 4.2开始)You cannot use read concern从封顶集合中读取时,不能使用读取关注"snapshot"when reading from a capped collection. (Starting in MongoDB 5.0)"snapshot"。(从MongoDB 5.0开始)You cannot read/write to collections in the无法读取/写入config,admin, orlocaldatabases.config、admin或local数据库中的集合。You cannot write to无法写入system.*collections.system.*集合。You cannot return the supported operation's query plan (i.e.无法返回支持的操作的查询计划(即explain).explain)。
For cursors created outside of a transaction, you cannot call对于在事务外部创建的游标,不能在事务内部调用getMoreinside the transaction.getMore。For cursors created in a transaction, you cannot call对于在事务中创建的游标,不能在事务外调用getMoreoutside the transaction.getMore。
Starting in MongoDB 4.2, you cannot specify从MongoDB 4.2开始,您不能将killCursorsas the first operation in a transaction.killCursors指定为事务中的第一个操作。
db.collection.aggregate() | aggregate | |
db.collection.countDocuments() | $match aggregation stage for the query and $group aggregation stage with a $sum expression to perform the count. $match聚合阶段和带有$sum表达式的$group聚合阶段来执行计数。 | |
db.collection.distinct() | distinct | $group stage. See Distinct Operation.$group阶段的聚合管道。请参见剔除重复操作。 |
db.collection.find() | find | |
db.collection.deleteMany()db.collection.deleteOne()db.collection.remove() | delete | |
db.collection.findOneAndDelete()db.collection.findOneAndReplace()db.collection.findOneAndUpdate() | findAndModify | upsert: true on a non-existing collection, the collection is implicitly created.upsert:true运行,则该集合是隐式创建的。upsert: true, the operation must be run on an existing collection. upsert:true,则该操作必须在现有集合上运行。Tip
DDL Operations |
db.collection.insertMany()db.collection.insertOne() | insert | Tip
DDL Operations |
db.collection.updateOne()db.collection.updateMany()db.collection.replaceOne() | update | Tip
DDL Operations |
db.collection.bulkWrite() | Tip
DDL Operations |
Operations that affect the database catalog, such as creating or dropping a collection or an index, are not allowed in multi-document transactions. 在多文档事务中,不允许执行影响数据库目录的操作,例如创建或删除集合或索引。For example, a multi-document transaction cannot include an insert operation that would result in the creation of a new collection. 例如,多文档事务不能包含会导致创建新集合的插入操作。See Restricted Operations.请参阅受限操作。
Informational commands, such as 事务中允许使用信息命令,如hello, buildInfo, connectionStatus (and their helper methods) are allowed in transactions; however, they cannot be the first operation in the transaction.hello、buildInfo、connectionStatus(及其辅助方法);但是,它们不能是事务中的第一个操作。
Read Preference读取首选项
Transactions support read preference 事务支持读取首选项primary.primary。
Atomicity原子性
While the transaction is open, no data changes made by operations in the transaction is visible outside the transaction:当事务处于打开状态时,事务中的操作在事务外不可见任何数据更改:
When a transaction commits, all data changes made in the transaction are saved and visible outside the transaction. That is, a transaction will not commit some of its changes while rolling back others.当事务提交时,事务中所做的所有数据更改都将保存并在事务外部可见。也就是说,事务在回滚其他更改时不会提交某些更改。Until a transaction commits, the data changes made in the transaction are not visible outside the transaction.在事务提交之前,在事务中所做的数据更改在事务外部是不可见的。However, when a transaction writes to multiple shards, not all outside read operations need to wait for the result of the committed transaction to be visible across the shards.然而,当一个事务写入多个分片时,并不是所有的外部读取操作都需要等待提交的事务的结果在分片中可见。For example, if a transaction is committed and write 1 is visible on shard A but write 2 is not yet visible on shard B, an outside read at read concern例如,如果事务已提交,并且写1在分片a上可见,但写2在分片B上还不可见,则外部读取时关注"local"can read the results of write 1 without seeing write 2."local"可以读取写1的结果,而不会看到写2。When a transaction aborts, all data changes made by the writes in the transaction are discarded without ever becoming visible and the transaction ends.当事务中止时,事务中写入的所有数据更改都将被丢弃,而不会变得可见,并且事务结束。
Example实例
Consider a scenario where as changes are made to an employee's record in the 考虑这样一种情况:当对hr database, you want to ensure that the events collection in the reporting database are in sync with the hr changes. hr数据库中的员工记录进行更改时,您希望确保reporting数据库中的events集合与hr更改同步。That is, you want to ensure that these writes are done as a single transaction, such that either both operations succeed or fail.也就是说,您希望确保这些写入是作为单个事务完成的,这样两个操作要么成功,要么失败。
The employees collection in the hr database has the following documents:hr数据库中的employees集合包含以下文档:
{ "_id" : ObjectId("5af0776263426f87dd69319a"), "employee" : 3, "name" : { "title" : "Mr.", "name" : "Iba Ochs" }, "status" : "Active", "department" : "ABC" }
{ "_id" : ObjectId("5af0776263426f87dd693198"), "employee" : 1, "name" : { "title" : "Miss", "name" : "Ann Thrope" }, "status" : "Active", "department" : "ABC" }
{ "_id" : ObjectId("5af0776263426f87dd693199"), "employee" : 2, "name" : { "title" : "Mrs.", "name" : "Eppie Delta" }, "status" : "Active", "department" : "XYZ" }
The events collection in the reporting database has the following documents:reporting数据库中的events集合包含以下文档:
{ "_id" : ObjectId("5af07daa051d92f02462644a"), "employee" : 1, "status" : { "new" : "Active", "old" : null }, "department" : { "new" : "ABC", "old" : null } }
{ "_id" : ObjectId("5af07daa051d92f02462644b"), "employee" : 2, "status" : { "new" : "Active", "old" : null }, "department" : { "new" : "XYZ", "old" : null } }
{ "_id" : ObjectId("5af07daa051d92f02462644c"), "employee" : 3, "status" : { "new" : "Active", "old" : null }, "department" : { "new" : "ABC", "old" : null } }
The following example opens a transaction, updates an employee's status to 以下示例打开一个事务,在Inactive in the employees status and inserts a corresponding document to the events collection, and commits the two operations as a single transaction.employees状态中将员工的状态更新为Inactive,并将相应的文档插入到events集合中,然后将这两个操作作为单个事务提交。
//Runs the txnFunc and retries if TransientTransactionError encountered如果遇到TransientTransactionError,则运行txnFunc并重试
function runTransactionWithRetry(txnFunc, session) {
while (true) {
try {
txnFunc(session); //performs transaction执行事务
break;
} catch (error) {
//If transient error, retry the whole transaction如果出现暂时错误,请重试整个事务
if ( error.hasOwnProperty("errorLabels") && error.errorLabels.includes("TransientTransactionError") ) {
print("TransientTransactionError, retrying transaction ...");
continue;
} else {
throw error;
}
}
}
}
//Retries commit if UnknownTransactionCommitResult encountered如果遇到UnknownTransactionCommitResult,则重试提交
function commitWithRetry(session) {
while (true) {
try {
session.commitTransaction(); //Uses write concern set at transaction start.在事务开始时使用写入关注集。
print("Transaction committed.");
break;
} catch (error) {
// Can retry commit
if (error.hasOwnProperty("errorLabels") && error.errorLabels.includes("UnknownTransactionCommitResult") ) {
print("UnknownTransactionCommitResult, retrying commit operation ...");
continue;
} else {
print("Error during commit ...");
throw error;
}
}
}
}
//Updates two collections in a transactions更新事务记录中的两个集合
function updateEmployeeInfo(session) {
employeesCollection = session.getDatabase("hr").employees;
eventsCollection = session.getDatabase("reporting").events;
session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } );
try{
employeesCollection.updateOne( { employee: 3 }, { $set: { status: "Inactive" } } );
eventsCollection.insertOne( { employee: 3, status: { new: "Inactive", old: "Active" } } );
} catch (error) {
print("Caught exception during transaction, aborting.");
session.abortTransaction();
throw error;
}
commitWithRetry(session);
}
//Start a session.启动会话。
session = db.getMongo().startSession( { readPreference: { mode: "primary" } } );
try{
runTransactionWithRetry(updateEmployeeInfo, session);
} catch (error) {
//Do something with error做一些错误的事情
} finally {
session.endSession();
}