目录
进入MongoDB中文手册(4.2版本)目录
本页列出了运行事务的一些生产注意事项。无论您在副本集或分片群集上运行事务,这些规则均适用。有关在分片群集上运行事务的信息,另请参见生产注意事项(分片群集),以获取特定于分片群集的其他注意事项。
1 可用性
- 在版本4.0中,MongoDB支持副本集上的多文档事务。
- 在4.2版中,MongoDB引入了分布式事务,它增加了对分片群集上多文档事务的支持,并包含了已有的对副本集上多文档事务的支持。
要在MongoDB 4.2部署(副本集和分片群集)上使用事务,客户端必须使用更新到MongoDB 4.2的MongoDB驱动程序。
分布式事务和多文档事务
从MongoDB 4.2开始,这两个术语是同义词。分布式事务是指分片群集和副本集上的多文档事务。从MongoDB 4.2开始,多文档事务(无论是在分片群集或副本集上)也称为分布式事务。
2 兼容性特性
要使用事务, 部署的所有成员的featureCompatibilityVersion必须至少为:
部署方式 | featureCompatibilityVersion最小值 |
---|---|
副本集 | 4.0 |
分片集群 | 4.2 |
要检查成员的fCV,请连接到该成员并运行以下命令:
db.adminCommand( { getParameter: 1, featureCompatibilityVersion: 1 } )
有关更多信息,请参见 setFeatureCompatibilityVersion参考页。
3 运行时限制
默认情况下,事务的运行时必须少于一分钟。您可以使用transactionLifetimeLimitSeconds为mongod实例修改此限制 。对于分片群集,必须修改所有分片副本集成员的此参数。超过此限制的事务将被视为已过期,并且将被定期清除程序中止。
对于分片群集,您还可以在提交事务上指定一个maxTimeMS限制。有关更多信息,请参阅分片群集事务时间限制。
4 OPLOG大小限制
从4.2版开始,
MongoDB创建封装事务中的所有写操作所需数量的oplog条目,而不是事务中所有写操作的单个条目。这消除了单个oplog条目对其所有写操作施加的事务的16MB总大小限制。尽管删除了总大小限制,但每个oplog条目仍必须在16MB的BSON文档大小限制内。
在4.0版中,
如果事务包含任何写操作,则MongoDB 在提交时将创建单个操作日志条目。即,事务中的各个操作没有相应的oplog条目。相反,单个操作日志条目包含事务中的所有写操作。事务的oplog条目必须在BSON文档大小限制内16MB。
5 WiredTiger缓存
为防止存储缓存压力对性能造成负面影响:
- 放弃事务时,中止事务。
- 当您在事务中进行单个操作时遇到错误时,请中止并重试该事务。
transactionLifetimeLimitSeconds还确保了过期的事务被定期中止,从而减轻存储缓存的压力。
6 事务和安全性
7 分片配置限制
您不能在具有设置writeConcernMajorityJournalDefault为false的分片的分片群集上运行事务(例如,具有使用内存存储引擎的投票成员的分片)。
8 分片集群和仲裁器
如果任何事务操作读取或写入包含仲裁程序的分片,则包含跨越多个分片的写操作的事务将出错并中止。
另请参阅三成员主次仲裁器体系结构,以了解对已禁用读策略“majority”的分片的事务限制。
9 三成员主次仲裁器架构
对于具有主次仲裁器(PSA)体系结构的三成员副本集或具有三成员PSA分片的分片群集,您可能已禁用了读策略“majority”来避免缓存压力。
在分片集群上
- 如果事务涉及已禁用读策略“majority”的分片,则不能对事务使用读策略"snapshot"。您只能将已读策略"local"或 “majority"用于事务。如果使用读策略"snapshot”,则事务错误并中止。
readConcern level 'snapshot' is not supported in sharded clusters when enableMajorityReadConcern=false.
- 如果任何事务的读或写操作涉及禁用了读策略"majority"的分片,则包含跨越多个分片的写操作的事务将出错并中止 。
在副本集上,
您可以指定读策略"local", “majority"或者"snapshot"甚至在副本集中已禁用的读策略"majority"。
但是,如果您打算对具有已禁用读策略"majority"分片的分片群集进行事务操作,则可能需要避免使用读策略"snapshot”。
提示
要检查是否已禁用读策略"majority",您可以在mongod实例上运行db.serverStatus()并检查storageEngine.supportsCommittedReads 字段。如果为false,则禁用了读策略"majority"。
也可以看看
- enableMajorityReadConcern false
- replication.enableMajorityReadConcern: false
10 获取锁
默认情况下,获取事务中的操作所需的锁需要等待长达5毫秒。如果事务无法在5毫秒内获得其所需的锁,则事务中止。
事务在中止或提交时释放所有锁。
提示
在紧接开始事务之前创建或删除集合时,如果在事务内访问了该集合,则发出具有写策略"majority"的create或drop操作,以确保该事务可以获取所需的锁。
锁请求超时
您可以使用maxTransactionLockRequestTimeoutMillis参数来调整事务等待获取锁的时间。通过增加maxTransactionLockRequestTimeoutMillis可以允许事务中的操作等待指定的时间来获取所需的锁。这可以在瞬时并发锁定获取时避免事务中止,例如快速运行的元数据操作。但是,这可能会延迟中止死锁的事务操作。
您还可以通过设置maxTransactionLockRequestTimeoutMillis为 -1来使用特定操作的超时时间( operation-specific timeout)。
11 待处理的DDL操作和事务
如果正在进行多文档事务,则影响相同数据库或集合的新DDL操作将在事务后面等待。当存在这些等待的DDL操作的时候,与等待的DDL操作访问相同数据库或集合的新事务无法获取所需的锁,并且将在等待maxTransactionLockRequestTimeoutMillis后中止。另外,访问相同数据库或集合的新的非事务操作将阻塞,直到达到maxTimeMS限制为止 。
请考虑以下情形:
- 需要集合锁的DDL操作:
在进行中的事务正在hr数据库中的employees集合上执行各种CRUD操作的同时,管理员对employees集合发出DDL操作db.collection.createIndex()。 createIndex()需要对集合使用排他的集合锁。
在进行中的事务完成之前,createIndex()操作必须等待获取锁。任何影响employees集合并在createIndex() 挂起时启动的新事务都必须等到createIndex()完成后再进行 。
待处理的DDL操作createIndex()不会影响hr数据库中其他集合上的事务。例如,hr数据库中contractors集合上的新事务可以正常启动和完成。 - 需要数据库锁定的DDL操作:
在进行中的事务对hr数据库中的employees集合执行各种CRUD操作时,管理员对同一数据库中的contractors集合执行DDL操作collMod。 collMod需要hr 数据库上的数据库锁。
在进行中的事务完成之前,collMod 操作必须等待获取锁。任何影响hr数据库或其任何集合以及在collMod挂起时启动的新事务都必须等到collMod完成之后再进行 。
在这两种情况下,如果DDL操作的等待超过 maxTransactionLockRequestTimeoutMillis,则在该操作之后等待的事务将中止。也就是说,maxTransactionLockRequestTimeoutMillis的值必须至少涵盖进行中的事务和挂起的DDL操作完成所需的时间。
也可以看看
- 进行中的事务和写冲突
- 进行中的事务和过时读
- 哪些管理命令可以锁定数据库?
- 哪些管理命令可以锁定集合?
12 进行中的事务和写冲突
如果事务正在进行中,并且在事务外部一个写操作去修改一个文档,而事务中的一个操作后续会尝试修改该文档,则事务会由于写冲突而中止。
如果事务正在进行中并且已锁定修改文档,则在事务外部写操作尝试修改同一文档时,该写操作将一直等到事务结束。
也可以看看
- 获取锁
- 待处理的DDL操作和事务
- $currentOp输出
13 进行中的事务和过时读
事务内的读操作可以返回过时的数据。也就是说,不能保证事务内的读操作会看到其他已提交事务执行的写操作或非事务性写操作。例如,请考虑以下顺序:
1)正在进行事务;
2)在事务外部的写操作删除一个文档;
3)事务内部的读操作能够读取已删除的文档,因为该操作写之前使用了快照(snapshot)。
为避免事务内部对单个文档进行过时的读,可以使用db.collection.findOneAndUpdate()方法。例如:
session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } );
employeesCollection = session.getDatabase("hr").employees;
employeeDoc = employeesCollection.findOneAndUpdate(
{ _id: 1, employee: 1, status: "Active" },
{ $set: { employee: 1 } },
{ returnNewDocument: true }
);
- 如果员工单据已在事务外更改,则事务中止。
- 如果员工文档未更改,则事务处理将返回该文档并锁定该文档。
14 正在进行的事务和块迁移
块迁移在某些阶段获得排他的集合锁。
如果正在进行的事务锁定了集合,并且涉及该集合的块迁移开始执行,则这些迁移阶段必须等待事务释放对集合的锁定,因此会影响块迁移的性能。
如果大块迁移与事务交错(例如,如果在大块迁移已在进行中而事务开始并且迁移在事务锁定集合之前完成迁移),则事务在提交期间出错并中止。
根据两个操作的交错方式,包括一些示例错误(错误消息已被缩写):
- an error from cluster data placement change … migration commit in progress for <namespace>
- Cannot find shardId the chunk belonged to at cluster time …
也可以看看
15 提交期间的外部读取
在提交事务期间,外部读取操作可能会尝试读取将会被事务修改的相同文档。如果事务写入多个分片,则在尝试进行分片提交时:
- 使用读策略snapshot或 "linearizable"或属于因果一致会话(即,包括afterClusterTime)的外部读操作等待事务的所有写入均可见。
- 使用其他读策略的外部读操作不等待事务的所有写入可见,而是读取事务前的可用文档。
16 错误
使用MongoDB 4.0驱动程序
要在MongoDB 4.2的部署(副本集和分片群集)上使用事务,客户端必须使用更新到MongoDB 4.2的MongoDB驱动程序。
在具有多个mongos实例的分片群集上,使用更新到MongoDB 4.0(而非MongoDB 4.2)的驱动程序执行事务将失败,并可能导致错误,包括:
注意
您的驱动程序可能会返回其他错误。有关详细信息,请参阅驱动程序的文档。
错误代码 | 错误信息 |
---|---|
251 | cannot continue txnId -1 for session … with txnId 1 |
50940 | cannot commit with no participants |
17 其他信息
也可以看看