Docs HomeMongoDB Manual

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开始,多文档事务可用于分片集群和副本集。

Important

Within 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.例如,多文档事务不能包含会导致创建新集合的插入操作。

The Session.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事务中支持的操作

Note

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.事务中使用的集合可以在不同的数据库中。

    Note

    You 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, or local databases.无法读取/写入configadminlocal数据库中的集合。
  • 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 getMore inside the transaction.对于在事务外部创建的游标,不能在事务内部调用getMore
  • For cursors created in a transaction, you cannot call getMore outside the transaction.对于在事务中创建的游标,不能在事务外调用getMore
Method方法Command命令Note备注
db.collection.aggregate()aggregateExcluding the following stages: 不包括以下阶段:
db.collection.countDocuments()Excluding the following query operator expressions: 排除以下查询运算符表达式: The method uses the $match aggregation stage for the query and $group aggregation stage with a $sum expression to perform the count. 该方法使用查询的$match聚合阶段和带有$sum表达式的$group聚合阶段来执行计数。
db.collection.distinct()distinctAvailable on unsharded collections. 可用于未排序的集合。
For sharded collections, use the aggregation pipeline with the $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()
findAndModifyStarting in MongoDB 4.4, if the update or replace operation is run with upsert: true on a non-existing collection, the collection is implicitly created.从MongoDB 4.4开始,如果更新或替换操作在不存在的集合上使用upsert:true运行,则该集合是隐式创建的。
In MongoDB 4.2 and earlier, if upsert: true, the operation must be run on an existing collection. 在MongoDB 4.2及更早版本中,如果upsert:true,则该操作必须在现有集合上运行。
Tip

See also: 另请参阅:

DDL Operations
db.collection.insertMany()
db.collection.insertOne()
insertStarting in MongoDB 4.4, if run on a non-existing collection, the collection is implicitly created.从MongoDB 4.4开始,如果在不存在的集合上运行,则会隐式创建该集合。
In MongoDB 4.2 and earlier, the operation must be run on an existing collection. 在MongoDB 4.2及更早版本中,操作必须在现有集合上运行。
Tip

See also: 另请参阅:

DDL Operations
db.collection.updateOne()
db.collection.updateMany()
db.collection.replaceOne()
updateStarting in MongoDB 4.4, if run on a non-existing collection, the collection is implicitly created.从MongoDB 4.4开始,如果在不存在的集合上运行,则会隐式创建该集合。
In MongoDB 4.2 and earlier, the operation must be run on an existing collection. 在MongoDB 4.2及更早版本中,操作必须在现有集合上运行。
Tip

See also: 另请参阅:

DDL Operations
db.collection.bulkWrite()
Various 各种各样的Bulk Operation Methods批量操作方法
Starting in MongoDB 4.4, if run on a non-existing collection, the collection is implicitly created.从MongoDB 4.4开始,如果在不存在的集合上运行,则会隐式创建该集合。
In MongoDB 4.2 and earlier, the operation must be run on an existing collection. 在MongoDB 4.2及更早版本中,操作必须在现有集合上运行。
Tip

See also: 另请参阅:

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.事务中允许使用信息命令,如hellobuildInfoconnectionStatus(及其辅助方法);但是,它们不能是事务中的第一个操作。

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 "local" can read the results of write 1 without seeing write 2.例如,如果事务已提交,并且写1在分片a上可见,但写2在分片B上还不可见,则外部读取时关注"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();
}