MongoDB事务
最后更新于
最后更新于
在MongoDB中,对单个文档的操作是原子的。由于可以在单个文档结构中使用内嵌文档和数组来获得数据之间的关系,而不必跨多个文档和集合进行范式化,所以这种单文档原子性避免了许多实际场景中对多文档事务的需求。
对于那些需要对多个文档(在单个或多个集合中)进行原子性读写的场景,MongoDB支持多文档事务。而使用分布式事务,事务可以跨多个操作、集合、数据库、文档和分片使用。
➤ 使用右上角的Select your language下拉菜单来设置以下示例的语言。
此示例突出显示了事务API的关键组件。
该示例使用新的回调API来进行事务处理,其中涉及启动事务、执行指定的操作并提交(或在出错时中止)。新的回调API还包含针对TransientTransactionError
或UnknownTransactionCommitResult
提交错误的重试逻辑。
重要
推荐。使用针对MongoDB部署版本更新的MongoDB驱动程序。对于MongoDB 4.2部署(副本集和分片集群上的事务,客户端必须使用为MongoDB 4.2更新的MongoDB驱动程序。
使用驱动程序时,事务中的每个操作必须与会话相关联(即将会话传递给每个操作)。
事务中的操作使用,[事务级写关注](https ://docs.mongodb.com/manual/core/transactions/#std-label-transactions-write-concern)和。
在MongoDB 4.2及更早版本中,你无法在事务中创建集合。如果在事务内部运行会导致文档插入的写操作(例如insert
或带有upsert: true
的更新操作),必须在已存在的集合上才能执行。
从MongoDB 4.4开始,你可以隐式或显式地在事务中创建集合。但是,必须使用针对4.4更新的MongoDB驱动程序。有关详细信息,请参阅。
同样请参阅:
说明
分布式事务和多文档事务
从MongoDB 4.2开始,这两个术语是同义词。分布式事务是指分片集群和副本集上的多文档事务。从MongoDB 4.2开始,多文档事务(无论是在分片集群上还是副本集上)也称为分布式事务。
对于多文档(在单个或多个集合中)读写上有原子性要求的场景,MongoDB提供了多文档事务支持:
在4.0版本中,MongoDB支持副本集上的多文档事务。
在4.2版本中,MongoDB引入了分布式事务,增加了对分片集群上多文档事务的支持,并合并了对副本集上多文档事务的现有支持。
为了在MongoDB 4.2部署(副本集和分片集群)上使用事务,客户端必须使用为MongoDB 4.2更新的MongoDB驱动程序。
Multi-document transactions are atomic (i.e. provide an "all-or-nothing" proposition):
多文档事务是原子的(即提供“全有或全无”的语义):
当事务提交时,事务中所做的所有数据更改都将保存并在事务外部可见。也就是说,事务不会在回滚其他更改时提交其某些更改。
在事务提交之前,事务中所做的数据更改在事务之外是不可见的。
当事务中止时,事务中所做的所有数据更改都将被丢弃,而不会变得可见。例如,如果事务中的任何操作失败,事务就会中止,并且事务中所做的所有数据更改都将被丢弃,而不会变得可见。
重要
TIP
同样请参阅:
分布式事务可用于跨多个操作、集合、数据库、文档以及从MongoDB 4.2开始可以跨分片。
对于事务:
事务中使用的集合可以位于不同的数据库中。
提示
你不能在跨分片的写事务中创建新集合。例如,如果你想对一个分片中已存在的集合进行写入且在另外一个不同的分片中隐式地创建集合,那么MongoDB无法在同一事务中执行这两种操作。
你不能读/写config
、admin
或local
数据库中的集合。
你不能写system.*
集合。
你不能返回这类支持操作的查询计划(即explain
)。
提示
提示
同样请参阅:
当在事务内部创建一个集合时:
一个不存在的集合。集合的创建是作为操作的一部分。
先前在同一事务中创建的新空集合。
限制
你不能在跨分片的写事务中创建新集合。例如,如果要对一个分片中已存在的集合执行写入操作且在另外一个不同的分片中隐式地创建集合,那么MongoDB无法在同一事务中执行这两种操作。
提示
同样请参阅:
为了在事务中执行一个distinct操作:
替代db.coll.distinct("x")
,请使用:
替代db.coll.distinct("x", { status: "A" })
,请使用:
管道将游标返回到文档:
迭代游标来访问结果集文档。
在4.4版本中变更。
下列这些操作在事务中是不被允许的:
在跨分片写入事务中创建新的集合。例如,如果在一个分片中对现有集合进行写入并在不同分片中隐式创建一个集合,则MongoDB无法在同一事务中执行这两种操作。
提示
同样请参阅:
事务是与某个会话相关联的;即你为一个会话启动一个事务。
在任何给定时间,一个会话最多可以有一个打开的事务。
使用驱动程序时,事务中的每个操作都必须与会话相关联。有关详细信息,请参阅你使用的驱动程序文档。
如果一个会话结束了并且它有一个打开的事务,则事务会中止。
如果事务级别的读偏好没有设置,事务会使用会话级别的读偏好。
如果事务级别的读关注没有设置,事务级的读关注默认为会话级的读关注。
事务支持下列的读关注级别:
"local"
"majority"
"snapshot"
提示
不要为事务内的单个写操作显式设置写关注。为事务内的单个写操作设置写关注会导致错误。
如果事务级别的写关注没有设置,事务级写关注默认为提交的会话级写关注。
w: 1
重要
w: "majority"
说明
如果任何事务操作从包含仲裁节点的分片读取或写入,其写操作跨越多个分片的事务将出错并中止。
在分片集群上,
在副本集上,
提示
说明
MongoDB提供了多种事务相关指标:
部署架构
最小featureCompatibilityVersion
副本集
4.0
分片集群
4.2
为了检查成员的FCV,连接到成员并运行下面的命令:
主节点使用WiredTiger存储引擎,同时
在MongoDB 4.0中,只有使用WiredTiger存储引擎的副本集支持事务。
说明
原文链接:https://docs.mongodb.com/manual/core/transactions/
译者:李正洋
有关shell中的示例,请参阅。
然而,当事务写入多个分片时,并非所有外部读取操作都需要等待已提交事务的结果在分片中可见。例如,如果事务已提交并且写入操作1在分片A上可见,但写入操作2在分片B上尚不可见,则外部读关注为的读操作可以读取写入操作1的结果,看不到写入操作2。
在大多数情况下,多文档事务比单文档写入会产生更大的性能成本,并且多文档事务的可用性不应替代有效的模型设计。对于许多场景, 依然会是最适合你的数据和用例。也就是说,对于许多场景,适当地对数据进行建模可以最大限度地减少对多文档事务的需求。
有关其他事务使用注意事项(例如runtime限制和oplog大小限制),另请参阅。
可以在现有集合上指定读/写(CRUD)操作。 有关CRUD操作的列表,请参阅。
当使用"4.4"
或更高版本时,可以在事务中创建集合和索引。详情请参考。
你不能写入集合。(从 MongoDB 4.2 开始)
对于在事务外部创建的游标,你不能在事务内部调用。
对于在事务内创建的游标,你不能在事务外调用。
从MongoDB 4.2开始,你不能将定义为中的第一个操作。
有关事务中不支持的操作列表,请参阅。
在开始事务之前立即创建或删除集合时,如果在事务内访问该集合,注意使用写关注来执行这些创建或删除操作,从而确保事务可以获取到所需要的锁。
从MongoDB 4.4开始,使用"4.4"
,可以在中创建集合和索引,除非事务是跨分片写入事务。如果使用"4.2"
或更低版本,事务中不允许使用影响数据库目录的操作,例如创建或删除集合和索引。
可以,例如:
针对一个不存在的集合上执行,或者
针对一个不存在集合上执行带有upsert: true
选项的。
可以使用命令或其帮助函数[db.createCollection()
](https://docs.mongodb.com/manual /reference/method/db.createCollection/#mongodb-method-db.createCollection)。
[[1]](https: //docs.mongodb.com/manual/core/transactions/#footnote-create-existing-index),要创建的索引需满足下面两者之一情况:
[]
还可以对现有索引运行和来检查是否存在。这些操作会成功地返回且不会创建索引。
要在事务内显式创建集合或索引,事务读关注级别必须为。显式创建是通过:
参数必须为true
(默认值)。在对分片集群设置参数时,请在所有分片上设置该参数。
要在事务中执行计数操作,请使用聚合阶段或带有[$sum
](https ://docs.mongodb.com/manual/reference/operator/aggregation/sum/#mongodb-group-grp.-sum)表达式的聚合阶段。
与4.0特性兼容的MongoDB驱动程序提供了一个集合级别的APIcountDocuments(filter, options)
作为带有表达式的[$group
](https://docs.mongodb.com/manual/reference/operator /aggregation/group/#mongodb-pipeline-pipe.-group)的帮助函数来执行计数。4.0驱动程序已弃用count()
API。
从MongoDB4.0.3开始,shell提供了在中使用带有[$sum
](https://docs.mongodb.com/ manual/reference/operator/aggregation/sum/#mongodb-group-grp.-sum)表达式的来执行计数的帮助函数。
对于未分片的集合,可以使用[db.collection.distinct()
](https://docs.mongodb.com/manual/reference/method/db.collection.distinct/#mongodb-method-db.collection .distinct)方法/命令以及带有阶段的聚合管道。
对于分片的集合,不能使用 方法或者命令。
要查找分片集合的不同值,请使用带阶段的聚合管道作为替代。例如:
信息类操作命令,比如, , (以及它们的辅助函数)是被允许在事务中使用的。然而,它们不能作为事务中的第一个操作。
影响数据库catalog的操作,例如在创建或删除集合和索引时使用"4.2"
或更低的。使用fcv"4.4"
或更高版本,可以在事务中创建集合和索引,除非事务是跨分片写入事务。有关详细信息,请参阅。
,例如方法和索引,例如和方法,当使用以外的读取关注级别时。
和命令及其辅助函数。
其他非CRUD和非信息类操作,比如,,等以及它们的辅助函数。
在事务中使用事务级的操作。
在使用驱动时,你可以在事务开始时设置事务级别的:
如果事务级别和会话级别的读偏好没有设置,事务使用客户端级别的读偏好。默认情况下,客户端级别的读偏好是。
包含读操作的必须使用读偏好。在一个给定事务中的所有操作都必须路由到同一个成员。
在事务中的操作会使用事务级。也就是说,在事务内部忽略在集合和数据库级别设置的任何读关注。
可以在事务开始时设置事务级别的。
如果事务级和会话级的读关注没有设置,事务级的读关注默认为客户端级的读关注。默认情况下,客户端级的读取关注是用于针对主节点的读取。同样请参阅:
读关注会返回节点最新可用的数据,但可能被回滚。
对于分片集群上的事务,读关注不能保证数据来自同一个跨分片的快照视图。如果需要快照隔离,请使用 读关注。
从MongoDB 4.4开始,使用"4.4"
或更高,可以在事务内。如果地创建集合或索引,事务必须使用读关注。地创建集合可使用任何适用于事务的读关注。
如果事务以的方式提交,则读关注会返回已被副本集中大多数成员确认的数据(即数据不会被回滚)。
如果事务不用的方式提交,读关注不能保证读操作读取到大多数已提交的数据。
对于分片集群上的事务,读关注不能保证数据来自同一个跨分片的快照视图。如果需要快照隔离,请使用读关注。
如果事务以的方式提交,则读关注会从一个大多数已提交数据的快照中返回数据。
如果事务不用的方式提交,读关注不能保证读操作读取到大多数已提交的数据。
对于分片集群上的事务,数据的视图是跨分片同步的。
事务使用事务级来提交写操作。事务内的写操作必须没有显式定义写关注,并使用默认的写关注。在提交时,然后使用事务级写关注提交写入。
可以在事务开始时设置事务级别的:
如果事务级写关注和会话级写关注没有设置,事务级写关注默认为客户端级写关注。默认情况下,客户端写关注为。也可以参考。
事务支持所有写关注的值,包括:
写关注会在提交已经被应用到主节点后反馈确认结果。
当使用提交,事务在发生故障时可能会回滚。
当使用写关注提交,事务级的读关注无法保证事务中的读操作能读取大多数已提交的数据。
当使用 写关注提交,事务级的读关注无法保证事务中的读操作能使用大多数已提交数据的快照。
写关注会在提交的数据被应用到大多数(M)有投票权的成员后返回确认;即提交数据已被应用到主节点和(M-1)有投票权的从节点。
当使用写关注提交时,事务级读关注可以确保操作能读取到大多数已提交的数据。对于分片集群上的事务,这种大多数已提交数据的视图在分片之间不会同步。
当使用写关注提交时,事务级读关注可以确保证操作能获取来自大多数已提交数据的同步快照。
不管,分片集群事务的提交操作包括一部分使用了{w: "majority", j: true}
写关注的操作。
关于使用事务的各种生产注意事项,请参阅。另外,如果是分片集群,同样请参阅。
另请参阅,了解在分片上已禁用读关注 majority的事务限制。
三成员PSA(主-从-仲裁)副本集或者拥有三成员PSA分片的分片集群可能已经禁用了读关注majority(或)
如果事务涉及到具有的分片,则不能对事务使用读关注。你只能对事务使用读关注或者。如果使用读关注,则事务会报错并中止。readConcern level 'snapshot' is not supported in sharded clusters when enableMajorityReadConcern=false
。如果事务的任何读取或写入操作涉及已禁用读关注"majority"
的分片,则其跨越多个分片进行写入操作的事务会出错并中止。
可以定义读关注、或者甚至在已的副本集上使用。但是,如果你计划迁移到有分片禁用读关注majority的分片集群上,可能希望避免使用读关注"snapshot"
。
要检查读关注"majority"是否被禁用,可以在实例上运行并检查字段。如果值为false
,则表示读关注"majority"已禁用。
更多信息请参考和。
不能在包含设置为false
分片的分片集群上运行事务(例如包含使用了作为投票成员的分片)。
不管,分片集群事务的提交操作都会包含一部分使用了{w: "majority", j: true}
写关注的操作。
方法中的命令
返回 相关指标。
聚合管道
如果操作作为事务的一部分则返回:。持有锁的的信息会作为事务的一部分。 是写入多个分片的分片事务的指标。
方法中的命令
如果操作作为事务的一部分则返回。是写入多个分片的分片事务的指标。
和日志信息
在日志组件下包含慢事务的信息(即事务超过了 阈值)
为了使用事务,部署架构中所有成员的至少为:
更多信息详见参考页。
从MongoDB 4.2开始,支持副本集和分片集群,其中:
从节点使用WiredTiger存储引擎或存储引擎。
你不能在包含设置为 false
分片的分片集群上运行事务,例如包含使用了作为投票成员的分片。
要了解有关何时使用事务以及它们是否支持你的用例的更多信息,请参阅来自MongoDB.live 2020的演示。