Docs 菜单
Docs 主页
/
MongoDB Manual
/ / / /

Session.startTransaction()

在此页面上

  • 定义
  • 兼容性
  • 行为
  • 例子
Session.startTransaction(<options>)

启动与会话关联的多文档事务。 在任何给定时间,一个会话最多可以有一个未结ACID 事务。

多文档事务适用于分片集群和副本集。

重要

在事务中,您只能指定对现有 集合的读取和写入 (CRUD) 操作。例如,多文档事务不能包含会导致创建新集合的插入操作。

Session.startTransaction() 方法可以利用以下选项获取一个文档:

{ readConcern: { level: <level>}, writeConcern: { w: <value>, j: <boolean>, wtimeout: <number> } }
选项
说明
readConcern

可选。用于为事务中所有操作指定读关注并且覆盖特定于操作的读关注的文档。

您可以指定以下读关注级别之一:

  • "snapshot"

  • "local"

  • "majority"

对于 "local""majority" 的读关注,MongoDB 有时可能会用更强烈的读关注代替。

writeConcern

可选。为事务指定写关注的文档。此写关注会应用于事务提交和中止操作。

事务中的操作使用 "w: 1",覆盖特定于操作的写关注。

如果您使用 "w: 1" 写关注进行提交,则您的事务可以在故障转移进程中回滚。

对于 MongoDB 驱动程序,事务默认使用客户端级别的写关注。

此方法可用于以下环境中托管的部署:

注意

如果使用访问控制运行,则必须具备事务操作的特权。

对于多文档事务

  • 可以在事务中创建集合和索引。有关详细信息,请参阅在事务中创建集合和索引

  • 事务中使用的集合可以位于不同的数据库中。

    注意

    您无法在跨分片写事务中创建新集合。例如,如果您在一个分片中写入一个现有集合,并在另一个分片中隐式创建一个集合,MongoDB 将无法在同一事务中执行这两个操作。

  • 不能写入固定大小集合。

  • 固定大小集合读取时不能使用读关注 "snapshot"。(从 MongoDB 5.0 开始)

  • 不能在 configadminlocal 数据库中读取/写入集合。

  • 不能写入 system.* 集合。

  • 不能使用 explain 或类似命令返回受支持操作的查询计划。

  • 对于在 ACID 事务外部创建的游标,无法在 ACID 事务内部调用 getMore

  • 对于在事务中创建的游标,无法在事务外部调用 getMore

方法
命令
注意

不包括以下查询运算符表达式:

该方法使用 $match 聚合阶段进行查询,使用 $group 聚合阶段和 $sum 表达式来执行计数。

可用于未分片的集合。

For sharded collections, use the aggregation pipeline with the $group stage. See Distinct Operation.

如果使用 upsert: true 在不存在的集合上运行更新或替换操作,则会隐式创建该集合。

有关详细信息,请参阅管理操作。

如果在不存在的集合上运行,则会隐式创建该集合。

有关详细信息,请参阅管理操作。

如果在不存在的集合上运行,则会隐式创建该集合。

有关详细信息,请参阅管理操作。

如果在不存在的集合上运行,则会隐式创建该集合。

有关详细信息,请参阅管理操作。

多文档事务中不允许影响数据库目录的操作,例如创建或删除集合或索引。例如,多文档事务不能包含会导致创建新集合的插入操作。请参阅受限操作。

事务中允许使用诸如 hellobuildInfoconnectionStatus(及其辅助方法)之类的信息命令,但它们不能是事务中的第一项操作。

事务支持读取偏好 primary

当事务处于打开状态时,在事务之外无法查看事务中操作所执行的任何数据更改:

  • 在事务提交时,事务中所做的所有数据更改都会保存,并且在事务之外可见。换言之,一个事务不会在回滚其他事务的同时提交某些更改。

    在事务进行提交前,在事务中所做的数据更改在事务外不可见。

    不过,当事务写入多个分片时,并非所有外部读取操作都需等待已提交事务的结果在各个分片上可见。例如,如果事务已提交并且写入 1 在分片 A 上可见,但写入 2 在分片 B 上尚不可见,则读关注 "local" 处的外部读取可以在不看到写入 2 的情况下读取写入 1 的结果。

  • 当事务中止时,事务中通过写入进行的所有数据更改都会丢弃且变得不可见,同时事务结束。

考虑这样一种情况:当对hr数据库中的员工记录进行更改时,您希望确保reporting数据库中的events集合与hr更改同步。换言之,您希望确保这些写入作为单个事务完成,以便两个操作要么成功,要么失败。

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" }

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 } }

以下示例将打开一个事务,在 employees 状态中将员工的状态更新为 Inactive,将相应文档插入到 events 集合,并将这两个操作作为单个事务提交。

// Runs the txnFunc and retries if TransientTransactionError encountered
function runTransactionWithRetry(txnFunc, session) {
while (true) {
try {
txnFunc(session); // performs transaction
break;
} catch (error) {
// If transient error, retry the whole transaction
if (error?.errorLabels?.includes("TransientTransactionError") ) {
print("TransientTransactionError, retrying transaction ...");
continue;
} else {
throw error;
}
}
}
}
// Retries commit if UnknownTransactionCommitResult encountered
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?.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();
}

提示

另请参阅:

后退

Session.commitTransaction()