Docs 菜单
Docs 主页
/

事务

在MongoDB中,对单个文档的操作具有原子性。由于您可以使用嵌入式文档和数组来捕获单个文档结构中数据之间的关系,而不是跨多个文档和集合进行规范化,因此对于许多实际使用案例来说,多文档事务并不是必需的。

对于需要对多个文档进行操作的原子性的情况, MongoDB支持事务。您可以跨多个操作、集合、数据库、文档和分片使用事务。

使用语言选择器设立以下示例的语言。

提示

有关mongosh mongosh中的示例,请参阅 示例。

对于需要对多个文档(在单个或多个集合中)原子性读取和写入的情况,MongoDB 支持分布式事务,包括副本集和分片集群上的事务。

分布式事务具有原子性:

  • 事务要么应用所有数据更改,要么回滚更改。

  • 在事务提交时,事务中所做的所有数据更改都会保存,并且在事务之外可见。

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

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

  • 当ACID 事务中止时,ACID 事务中所做的所有数据更改都将被丢弃,并且永远不可见。

重要

在大多数情况下,与单文档写入操作相比,分布式事务会产生更高的性能成本,并且分布式事务的可用性不应取代有效的模式设计。在许多情况下,非规范化数据模型(嵌入式文档和数组)仍然是数据和使用案例的最佳选择。换言之,对于许多场景,适当的数据建模将最大限度地减少对分布式事务的需求。

有关其他事务使用注意事项(如运行时间限制和 oplog 大小限制),另请参阅生产注意事项

事务可用于多个操作、集合、数据库、文档和分片。

对于事务:

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

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

    注意

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

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

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

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

  • 不能写入 system.* 集合。

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

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

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

  • 您不能将 killCursors 命令指定为事务中的第一个操作。

    此外,如果在ACID 事务中运行killCursors 命令,服务器会立即停止指定的游标。它不会等待ACID 事务提交。

有关事务中不支持的操作列表,请参阅限制操作。

提示

在启动事务之前创建或删除集合时,如果在事务内部访问该集合,请发出带有写关注 "majority" 的创建或删除操作,以确保事务可以获取所需的锁。

如果不是跨分片写事务(write transaction),则可以在ACID 事务中执行以下操作:

  • 创建集合。

  • 在先前同一事务中创建的新空集合上创建索引。

在事务中创建集合时:

在事务内创建索引 [1] 时,要创建的索引必须位于以下位置之一:

  • 不存在的集合。集合作为操作的一部分创建。

  • 先前在同一事务中创建的新空集合。

[1] 您还可以对现有索引运行 db.collection.createIndex()db.collection.createIndexes() 以检查其是否存在。这些操作成功返回而不创建索引。
  • 您无法在跨分片写事务中创建新集合。例如,如果您在一个分片中写入一个现有集合,并在另一个分片中隐式创建一个集合,MongoDB 将无法在同一事务中执行这两个操作。

  • 当以分片集合为目标时,您无法在事务中使用 $graphLookup 阶段。

  • 要在事务内显式创建集合或索引,事务读关注级别必须为 "local"

    要显式创建集合和索引,请使用以下命令和方法:

    命令
    方法

要在事务内执行计数操作,请使用 $count 聚合阶段或 $group(带有 $sum 表达式)聚合阶段。

MongoDB 驱动程序提供集合级 API countDocuments(filter, options) 作为辅助方法,该方法使用 $group$sum 表达式来执行计数。

mongosh 提供 db.collection.countDocuments() 辅助方法,该方法使用 $group$sum 表达式进行计数。

如要在事务中执行不同的操作:

  • 对于未分片的集合,可以使用 db.collection.distinct() 方法/distinct 命令以及带有 $group 阶段的聚合管道。

  • 对于分片集合,不能使用 db.collection.distinct() 方法或 distinct 命令。

    要查找分片集合的不同值,请改用带有 $group 阶段的 aggregation pipeline。例如:

    • 不使用 db.coll.distinct("x"),而是使用

      db.coll.aggregate([
      { $group: { _id: null, distinctValues: { $addToSet: "$x" } } },
      { $project: { _id: 0 } }
      ])
    • 不使用 db.coll.distinct("x", { status: "A" }),而是使用

      db.coll.aggregate([
      { $match: { status: "A" } },
      { $group: { _id: null, distinctValues: { $addToSet: "$x" } } },
      { $project: { _id: 0 } }
      ])

    管道返回一个指向文档的游标:

    { "distinctValues" : [ 2, 3, 1 ] }

    迭代游标以访问结果文档。

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

事务中不允许执行以下操作:

  • 事务与会话关联。

  • 一个会话一次最多可以具有一个未结事务。

  • 使用驱动程序时,事务中的每项操作都必须与会话关联。有关详细信息,请参阅驱动程序特定文档。

  • 如果会话结束并且具有打开的事务,则事务将中止。

事务中的操作使用事务级读取偏好

使用驱动程序,您可以在事务启动时设置事务级读取偏好

  • 如果未设置事务级别的读取偏好,则事务将使用会话级别的读取偏好。

  • 如果未设置事务级别和会话级别的读取偏好,则事务将使用客户端级别的读取偏好。默认情况下,客户端级别的读取偏好为 primary

包含读取操作的事务必须使用读取偏好(read preference)primary。给定ACID 事务中的所有操作必须路由到同一节点。

事务中的操作使用事务级读关注。也就是说,在集合和数据库级别设置的任何读关注在事务中都会被忽略。

您可以在事务启动时设置事务级别的读关注

  • 如果未设置事务级别的读关注,则事务级别的读关注默认为会话级别的读关注。

  • 如果未设置事务级读关注和会话级读关注,则事务级读关注默认为客户端级读关注。默认情况下,对于主节点上的读取,客户端级读关注是 "local"。另请参阅:

事务支持以下读关注级别:

  • 读关注 "local" 返回节点中可用的最新数据,但可以回滚。

  • 在副本集上,即使ACID 事务使用读关注(read concern)local ,您也可能会观察到更强的读隔离性性,因为该操作会从ACID 事务打开点的快照中读取。

  • 对于分片集群上的事务,读关注 "local" 无法保证数据来自跨分片的同一快照视图。如果需要快照隔离,请使用读关注 "snapshot"

  • 您可以在事务中创建集合和索引。如要显式创建集合或索引,则事务必须使用读关注 "local"。如果隐式创建集合,则可以使用任何可用于事务的读关注。

  • 如果事务以写关注“majority”提交,则读关注 "majority" 返回已被多数副本集节点确认且无法回滚的数据。否则,读关注 "majority" 不保证读取操作读取多数提交的数据。

  • 对于分片集群上的事务,读关注 "majority" 无法保证数据来自跨分片的同一快照视图。如果需要快照隔离,请使用读关注 "snapshot"

事务使用事务级写关注(write concern)来提交写入操作。事务内的写入操作必须在没有显式写关注(write concern)规范的情况下运行,并使用默认的写关注(write concern)。在提交时,使用事务级写关注(write concern)提交写入。

提示

请勿为事务中的各个写入操作显式设置写关注。为事务内的各个写入操作设置写关注会返回错误消息。

您可以在事务启动时设置事务级写关注

  • 如果未设置事务级别的写关注,则事务级别的写关注默认为提交的会话级别写关注。

  • 如果未设置事务级别的写关注和会话级别的的写关注,则事务级别的写关注默认为 的客户端级别的写关注,

事务支持所有写关注 w 值,包括:

  • 写关注 w: 1 会在提交应用于主节点后返回确认信息。

    重要

  • 使用w: 1写入关注提交时,事务级 "majority" 读关注无法保证事务中的读操作会读取大多数已提交数据。

  • 使用 w: 1 写关注提交时,事务级 "snapshot" 读关注无法保证事务中的读操作会使用大多数已提交数据的快照。

  • 在将提交应用于大多数投票节点后,写关注 w: "majority" 会返回确认消息。

  • 使用 w: "majority" 写关注提交时,事务级 "majority" 读关注可以保证操作已读取大多数已提交数据。对于分片集群上的事务,大多数已提交数据的视图不会在各分片之间同步。

  • 使用 w: "majority" 写关注提交时,事务级 "snapshot" 读关注可以保证操作已从大多数已提交数据的同步快照中读取。

注意

无论为事务指定了何种写关注,分片集群事务的提交操作始终包括一些使用 {w: "majority", j: true} 写关注的部分。

服务器参数 coordinateCommitReturnImmediatelyAfterPersistingDecision 可控制何时将事务提交决策返回给客户端。

该参数是在 MongDB 5.0 中引入的,默认值为 true。。在 MongoDB 6.1 中,该默认值更改为 false

coordinateCommitReturnImmediatelyAfterPersistingDecisionfalse 时,分片事务协调器会等待所有成员确认事务提交,然后再将提交决策返回给客户端。

如果您为写入指定"majority"写关注(write concern),并且该操作在返回响应之前没有复制到计算出的多数副本集成员,则数据最终会复制或回滚。请参阅wtimeout

无论为事务指定了何种写关注 ,驱动程序在重试 commitTransaction 时都会应用 w: "majority" 作为写关注。

以下部分描述了因部署类型、存储引擎和MongoDB版本而异的ACID 事务要求和注意事项。

有关生产环境中的事务,请参阅生产环境注意事项。此外,有关分片集群,请参阅生产环境注意事项(分片集群)

如果副本集具有仲裁节点节点,则无法使用ACID 事务更改分分片键。 仲裁节点无法参与多分片事务所需的数据操作。

如果任何事务操作读取或写入包含仲裁节点的分片,则写入操作跨越多个分片的事务将出现错误并中止。

您无法在具有将 writeConcernMajorityJournalDefault 设置为 false 的分片(例如具有使用内存中存储引擎的投票节点的分片)的分片集群上运行事务。

注意

无论为事务指定了何种写关注,分片集群事务的提交操作始终包括一些使用 {w: "majority", j: true} 写关注的部分。

要获取事务状态和指标,请使用以下方法:

返回:

返回事务指标。

MongoDB Atlas免费集群或 Flex 集群不会返回某些 serverStatus 响应字段。有关更多信息,请参阅MongoDB Atlas文档中的有限命令

$currentOp 聚合管道

返回:

返回:

mongodmongos 日志消息

TXN 日志组件中包含有关慢速事务(即超过 operationProfiling.slowOpThresholdMs 阈值的事务)的信息。

要使用事务,所有部署节点的 featureCompatibilityVersion 必须至少为:

部署
最低 featureCompatibilityVersion

副本集(Replica Set)

4.0

分片集群

4.2

要检查成员的 FCV,请连接到该成员并运行以下命令:

db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )

更多信息,请参阅 setFeatureCompatibilityVersion 参考页。

事务 在以下情况下,副本集和分片的集群支持:

  • 主节点使用 WiredTiger 存储引擎,而

  • 从节点使用 WiredTiger 存储引擎或内存存储引擎。

注意

您无法在具有将 writeConcernMajorityJournalDefault 设置为 false 的分片(例如具有使用内存中存储引擎的投票成员的分片)的分片集群上运行事务。

从 MongoDB 5.2(和 5.0.4)开始:

  • 当查询访问分片时,数据段迁移DDL 操作可能会占用集合的关键部分。

  • 要限制分片在事务中等待关键部分的时间,使用 metadataRefreshInTransactionMaxWaitMS 参数。

    注意

    从MongoDB 8.1 开始,旧的 metadataRefreshInTransactionMaxWaitBehindCritSecMS 参数已重命名为 metadataRefreshInTransactionMaxWaitMS。您可以继续使用 metadataRefreshInTransactionMaxWaitBehindCritSecMS 作为参数名称,但它已弃用,并将在未来的MongoDB发布中删除。

后退

update

在此页面上