在MongoDB中,对单个文档的操作具有原子性。由于您可以使用嵌入式文档和数组来捕获单个文档结构中数据之间的关系,而不是跨多个文档和集合进行规范化,因此对于许多实际使用案例来说,多文档事务并不是必需的。
对于需要对多个文档进行操作的原子性的情况, MongoDB支持事务。您可以跨多个操作、集合、数据库、文档和分片使用事务。
MongoDB支持ACID事务。事务根据配置的读关注和写关注(write concern)设置提供原子性、一致性、隔离性和持久性。
事务 API
使用语言选择器设置以下示例的语言。
提示
For an example in mongosh, see mongosh Example.
事务和原子性
对于需要对多个文档(在单个或多个集合中)原子性读取和写入的情况,MongoDB 支持分布式事务,包括副本集和分片集群上的事务。
分布式事务具有原子性:
事务要么应用所有数据更改,要么回滚更改。
在事务提交时,事务中所做的所有数据更改都会保存,并且在事务之外可见。
在事务进行提交前,在事务中所做的数据更改在事务外不可见。
不过,当事务写入多个分片时,并非所有外部读取操作都需等待已提交事务的结果在各个分片上可见。例如,如果事务已提交并且写入 1 在分片 A 上可见,但写入 2 在分片 B 上尚不可见,则读关注
"local"处的外部读取可以在不看到写入 2 的情况下读取写入 1 的结果。当ACID 事务中止时,ACID 事务中所做的所有数据更改都将被丢弃,并且永远不可见。
重要
在大多数情况下,与单文档写入操作相比,分布式事务会产生更高的性能成本,并且分布式事务的可用性不应取代有效的模式设计。在许多情况下,非规范化数据模型(嵌入式文档和数组)仍然是数据和使用案例的最佳选择。换言之,对于许多场景,适当的数据建模将最大限度地减少对分布式事务的需求。
有关其他事务使用注意事项(如运行时间限制和 oplog 大小限制),另请参阅生产注意事项。
事务和操作
事务可用于多个操作、集合、数据库、文档和分片。
对于事务:
可以在事务中创建集合和索引。有关详细信息,请参阅在事务中创建集合和索引
事务中使用的集合可以位于不同的数据库中。
注意
您无法在跨分片写事务中创建新集合。例如,如果您在一个分片中写入一个现有集合,并在另一个分片中隐式创建一个集合,MongoDB 将无法在同一事务中执行这两个操作。
不能写入固定大小集合。
从固定大小集合读取时不能使用读关注
"snapshot"。(从 MongoDB 5.0 开始)不能在
config、admin或local数据库中读取/写入集合。不能写入
system.*集合。不能使用
explain或类似命令返回受支持操作的查询计划。
- You cannot specify the
killCursorscommand as the first operation in a transaction. Additionally, if you run thekillCursorscommand within a transaction, the server immediately stops the specified cursors. It does not wait for the transaction to commit.
有关事务中不支持的操作列表,请参阅限制操作。
提示
在启动事务之前创建或删除集合时,如果在事务内部访问该集合,请发出带有写关注 "majority" 的创建或删除操作,以确保事务可以获取所需的锁。
提示
在事务中创建集合和索引
如果不是跨分片写事务(write transaction),则可以在ACID 事务中执行以下操作:
创建集合。
在先前同一事务中创建的新空集合上创建索引。
在事务中创建集合时:
您可以隐式创建一个集合,例如:
对不存在的集合进行插入操作,或
对不存在的集合使用
upsert: true进行 update/findAndModify 操作。
您可以使用
create命令或其辅助程序db.createCollection()显式创建集合。
在事务内创建索引 [1] 时,要创建的索引必须位于以下位置之一:
不存在的集合。集合作为操作的一部分创建。
先前在同一事务中创建的新空集合。
| [1] | 您还可以对现有索引运行 db.collection.createIndex() 和 db.collection.createIndexes() 以检查其是否存在。这些操作成功返回而不创建索引。 |
限制
您无法在跨分片写事务中创建新集合。例如,如果您在一个分片中写入一个现有集合,并在另一个分片中隐式创建一个集合,MongoDB 将无法在同一事务中执行这两个操作。
当以分片集合为目标时,您无法在事务中使用
$graphLookup阶段。要在事务内显式创建集合或索引,事务读关注级别必须为
"local"。要显式创建集合和索引,请使用以下命令和方法:
提示
计数操作
要在事务内执行计数操作,请使用 $count 聚合阶段或 $group(带有 $sum 表达式)聚合阶段。
MongoDB 驱动程序提供集合级 API countDocuments(filter, options) 作为辅助方法,该方法使用 $group 和 $sum 表达式来执行计数。
mongosh provides the db.collection.countDocuments() helper method that uses the $group with a $sum expression to perform a count.
去重操作
如要在事务中执行不同的操作:
对于未分片的集合,可以使用
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 ] } 迭代游标以访问结果文档。
信息操作
事务中允许使用诸如 hello、buildInfo、connectionStatus(及其辅助方法)之类的信息命令,但它们不能是事务中的第一项操作。
限制性操作
事务中不允许执行以下操作:
在跨分片写事务中创建新集合。例如,如果您在一个分片中写入一个现有集合,并在另一个分片中隐式创建一个集合,那么 MongoDB 将无法在同一事务中执行这两项操作。
使用
"local"以外的读关注级别时,显式创建集合(例如db.createCollection()方法)和索引(例如db.collection.createIndexes()和db.collection.createIndex()方法)。listCollections和listIndexes命令及其辅助方法。其他非 CRUD 和非信息性操作(例如
createUser、getParameter和count)及其辅助程序。并行操作。要同时更新多个命名空间,请考虑改用
bulkWrite命令。
事务和会话
事务与会话关联。
一个会话一次最多可以具有一个未结事务。
使用驱动程序时,事务中的每项操作都必须与会话关联。有关详细信息,请参阅驱动程序特定文档。
如果会话结束并且具有打开的事务,则事务将中止。
读关注(read concern)、写关注(write concern)和读取偏好(read preference)
事务和读取偏好
事务中的操作使用事务级读取偏好。
使用驱动程序,您可以在事务启动时设置事务级读取偏好:
如果未设置事务级别的读取偏好,则事务将使用会话级别的读取偏好。
如果未设置事务级别和会话级别的读取偏好,则事务将使用客户端级别的读取偏好。默认情况下,客户端级别的读取偏好为
primary。
Transactions that contain read operations must use read preference primary. All operations in a given transaction must route to the same member.
事务和读关注
事务中的操作使用事务级读关注。也就是说,在集合和数据库级别设置的任何读关注在事务中都会被忽略。
您可以在事务启动时设置事务级别的读关注。
如果未设置事务级别的读关注,则事务级别的读关注默认为会话级别的读关注。
事务支持以下读关注级别:
"local"
读关注
"local"返回节点中可用的最新数据,但可以回滚。在副本集上,即使ACID 事务使用读关注(read concern)
local,您也可能会观察到更强的读隔离性性,因为该操作会从ACID 事务打开点的快照中读取。For transactions on sharded cluster,
"local"read concern cannot guarantee that the data is from the same snapshot view across the shards. If snapshot isolation is required, use"snapshot"read concern.You can create collections and indexes inside a transaction. If explicitly creating a collection or an index, the transaction must use read concern
"local". If you implicitly create a collection, you can use any of the read concerns available for transactions.
"majority"
If the transaction commits with write concern "majority", read concern
"majority"returns data that has been acknowledged by a majority of the replica set members and can't be rolled back. Otherwise, read concern"majority"provides no guarantees that read operations read majority-committed data.For transactions on sharded cluster, read concern
"majority"can't guarantee that the data is from the same snapshot view across the shards. If snapshot isolation is required, use read concern"snapshot".
"snapshot"
Read concern
"snapshot"returns data from a snapshot of majority committed data if the transaction commits with write concern "majority".If the transaction does not use write concern "majority" for the commit, the
"snapshot"read concern provides no guarantee that read operations used a snapshot of majority-committed data.对于分片集群上的事务,数据的
"snapshot"视图会在各分片之间同步。
事务和写关注
事务使用事务级写关注(write concern)来提交写入操作。事务内的写入操作必须在没有显式写关注(write concern)规范的情况下运行,并使用默认的写关注(write concern)。在提交时,使用事务级写关注(write concern)提交写入。
提示
请勿为事务中的各个写入操作显式设置写关注。为事务内的各个写入操作设置写关注会返回错误消息。
您可以在事务启动时设置事务级写关注。
如果未设置事务级别的写关注,则事务级别的写关注默认为提交的会话级别写关注。
如果未设置事务级别的写关注和会话级别的的写关注,则事务级别的写关注默认为 的客户端级别的写关注,
w: "majority"(在 MongoDB 5.0 及更高版本中),包含仲裁节点的部署有所不同。请参阅隐式默认写关注。
事务支持所有写关注 w 值,包括:
w: 1
写关注
w: 1会在提交应用于主节点后返回确认信息。重要
使用
w: 1提交时,如果发生故障转移,则可以回滚事务。使用
w: 1写入关注提交时,事务级"majority"读关注无法保证事务中的读操作会读取大多数已提交数据。使用
w: 1写关注提交时,事务级"snapshot"读关注无法保证事务中的读操作会使用大多数已提交数据的快照。
w: "majority"
在将提交应用于大多数投票节点后,写关注
w: "majority"会返回确认消息。使用
w: "majority"写关注提交时,事务级"majority"读关注可以保证操作已读取大多数已提交数据。对于分片集群上的事务,大多数已提交数据的视图不会在各分片之间同步。使用
w: "majority"写关注提交时,事务级"snapshot"读关注可以保证操作已从大多数已提交数据的同步快照中读取。
注意
Regardless of the write concern specified for the transaction, the commit operation for a sharded cluster transaction includes some parts that use {w: "majority", j: true} write concern.
服务器参数 coordinateCommitReturnImmediatelyAfterPersistingDecision 可控制何时将事务提交决策返回给客户端。
该参数是在 MongDB 5.0 中引入的,默认值为 true。。在 MongoDB 6.1 中,该默认值更改为 false。
当 coordinateCommitReturnImmediatelyAfterPersistingDecision 为 false 时,分片事务协调器会等待所有成员确认事务提交,然后再将提交决策返回给客户端。
如果您为写入指定"majority"写关注(write concern),并且该操作在返回响应之前没有复制到计算出的多数副本集成员,则数据最终会复制或回滚。请参阅wtimeout。
Regardless of the write concern specified for the transaction, the driver applies w: "majority" as the write concern when retrying commitTransaction.
事务注意事项
以下部分描述了因部署类型、存储引擎和MongoDB版本而异的ACID 事务要求和注意事项。
生产环境注意事项
有关生产环境中的事务,请参阅生产环境注意事项。此外,有关分片集群,请参阅生产环境注意事项(分片集群)。
仲裁节点
Since arbiters are voting replicas that don't contain data, MongoDB protects your data and prevents multi-shard data transactions from targeting shards whose replica sets include an arbiter. This includes changing a shard key using a transaction.
如果任何ACID 事务操作读取或写入包含仲裁节点的分片,MongoDB会阻止写入操作跨越多个分片的事务。
分片配置限制
To run transactions on a sharded cluster, writeConcernMajorityJournalDefault must be set to true.
注意
Regardless of the write concern specified for the transaction, the commit operation for a sharded cluster transaction includes some parts that use {w: "majority", j: true} write concern.
诊断
要获取事务状态和指标,请使用以下方法:
源 | 返回: |
|---|---|
| |
| 返回:
|
| 返回:
|
在 |
特征兼容性版本 (FCV)
要使用事务,所有部署节点的 featureCompatibilityVersion 必须至少为:
部署 | 最低 featureCompatibilityVersion |
|---|---|
副本集(Replica Set) |
|
分片集群 |
|
要检查成员的 FCV,请连接到该成员并运行以下命令:
db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )
更多信息,请参阅 setFeatureCompatibilityVersion 参考页。
存储引擎
事务 在以下情况下,副本集和分片的集群支持:
主节点使用 WiredTiger 存储引擎,而
从节点使用 WiredTiger 存储引擎或内存存储引擎。
注意
您无法在具有将 writeConcernMajorityJournalDefault 设置为 false 的分片(例如具有使用内存中存储引擎的投票成员的分片)的分片集群上运行事务。
限制关键部分等待时间
从 MongoDB 5.2(和 5.0.4)开始:
要限制分片在事务中等待关键部分的时间,使用
metadataRefreshInTransactionMaxWaitMS参数。注意
从MongoDB 8.1 开始,旧的
metadataRefreshInTransactionMaxWaitBehindCritSecMS参数已重命名为metadataRefreshInTransactionMaxWaitMS。您可以继续使用metadataRefreshInTransactionMaxWaitBehindCritSecMS作为参数名称,但它已弃用,并将在未来的MongoDB发布中删除。