文档菜单
文档首页
/
MongoDB 手册
/

生产注意事项

在本页

  • 可用性
  • 功能兼容性
  • 运行时限制
  • oplog 大小限制
  • WiredTiger 缓存
  • 事务和安全
  • 分片配置限制
  • 分片集群和仲裁者
  • 获取锁
  • 挂起的 DDL 操作和事务
  • 进行中事务和写冲突
  • 进行中事务和陈旧读取
  • 进行中事务和数据块迁移
  • 提交期间的外部读取
  • 更多信息

以下页面列出了一些运行事务的生产注意事项。这些适用于您在副本集或分片集群上运行事务的情况。有关在分片集群上运行事务的信息,请参阅生产注意事项(分片集群),其中包含针对分片集群的特定考虑因素。

  • MongoDB 支持副本集上的多文档事务。

  • 分布式事务增加了对分片集群上多文档事务的支持,并整合了对副本集上多文档事务的现有支持。

注意

分布式事务和多文档事务

这两个术语是同义的。分布式事务指的是分片集群和副本集上的多文档事务。多文档事务(无论是在分片集群还是副本集上)也称为分布式事务。

要使用事务,部署中所有成员的 featureCompatibilityVersion 必须至少为

部署
最小featureCompatibilityVersion
副本集
4.0
分片集群
4.2

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

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

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

注意

要配置 MongoDB Atlas 中的最大事务寿命,请参阅 Atlas 文档中的设置事务寿命

默认情况下,事务的运行时间必须少于一分钟。您可以使用 transactionLifetimeLimitSeconds 修改此限制,该参数适用于 mongod 实例。对于分片集群,必须对所有分片副本集成员修改此参数。超过此限制的事务被视为已过期,并将由定期清理过程中止。

对于分片集群,您还可以在 commitTransaction 上指定 maxTimeMS 限制。有关更多信息,请参阅 分片集群事务时间限制。

MongoDB 会创建尽可能多的 oplog 条目来封装事务中的所有写操作,而不是为事务中的所有写操作创建单个条目。这消除了由所有写操作的单个 oplog 条目强制的 16MB 总大小限制。尽管总大小限制被移除,但每个 oplog 条目仍然必须符合 16MB 的 BSON 文档大小限制。

为了防止存储缓存压力对性能产生负面影响

  • 当您放弃一个事务时,终止事务。

  • 当您在事务中的单个操作遇到错误时,终止并重试事务。

transactionLifetimeLimitSeconds 还确保定期终止已过期的交易以缓解存储缓存压力。

注意

如果您有一个未提交的事务导致对 WiredTiger 缓存 压力过大,该事务将终止并返回一个 写冲突 错误。

如果一个事务太大而无法放入 WiredTiger 缓存,事务将终止并返回一个 TransactionTooLargeForCache 错误。

  • 如果以访问控制方式运行,您必须拥有对内置角色的权限,以便在交易中执行操作

  • 如果以审计方式运行,则已中止的交易中的操作仍会被审计。然而,没有审计事件表明交易已中止。

您不能在具有将writeConcernMajorityJournalDefault设置为false的分片(例如,具有使用内存存储引擎的投票成员的分片)上运行交易。

如果副本集包含仲裁者,则无法使用事务更改分片键。仲裁者无法参与多分片事务所需的数据操作。

如果事务的写操作跨越多个分片,并且任何事务操作从包含仲裁者的分片读取或写入,则会出错并中止。

默认情况下,事务等待最多5毫秒以获取事务操作所需的锁。如果事务在5毫秒内无法获取所需的锁,则事务将中止。

事务在中止或提交时会释放所有锁。

提示

在开始事务之前立即创建或删除集合时,如果事务中访问了该集合,则在创建或删除操作中使用写关注"majority"以确保事务可以获取所需的锁。

注意

MongoDB Atlas集群限制使用setParameter命令。有关更多信息,请参阅Atlas文档中的Atlas不支持命令

要修改您的Atlas集群参数,请联系Atlas支持

您可以使用 maxTransactionLockRequestTimeoutMillis 参数来调整事务等待获取锁的时长。增加 maxTransactionLockRequestTimeoutMillis 允许事务中的操作等待指定的时间来获取所需的锁。这可以帮助避免在短暂的并发锁获取时的事务中断,例如快速运行的元数据操作。然而,这可能会延迟死锁事务操作的终止。

您还可以通过将 maxTransactionLockRequestTimeoutMillis 设置为 -1 来使用特定于操作的超时。

如果正在执行多文档事务,则影响相同数据库(或集合)的新DDL操作将在事务之后等待。在存在这些挂起的DDL操作期间,无法获取所需锁的新事务将无法访问与挂起的DDL操作相同的数据库(或集合),并在等待 maxTransactionLockRequestTimeoutMillis 后终止。此外,访问相同数据库(或集合)的新非事务操作将阻塞,直到达到它们的 maxTimeMS 限制。

考虑以下场景

需要集合锁的DDL操作

在事务正在对 hr 数据库中的 employees 集合执行各种CRUD操作时,管理员对 employees 集合执行了 db.collection.createIndex() DDL操作。 createIndex() 需要集合的排他性锁。

在事务完成之前,createIndex() 操作必须等待获取锁。任何在 createIndex() 挂起期间开始影响 employees 集合的新事务必须等待 createIndex() 完成。

挂起的 createIndex() DDL操作不会影响 hr 数据库中其他集合的事务。例如,在 hr 数据库中的 contractors 集合上启动的新事务可以像往常一样开始和完成。

需要数据库锁的DDL操作

当一个正在进行的事务在hr数据库中的employees集合上执行各种CRUD操作时,管理员发出renameCollection DDL操作,将vendors.contractors集合重命名为hr.contractors。当目标数据库(hr)与源数据库(vendors)不同时,renameCollection需要锁定目标数据库。

在正在进行的交易完成之前,renameCollection操作必须等待获取锁。在renameCollection挂起期间,任何影响hr数据库或其任何集合的新事务都必须等待renameCollection完成。

在任一场景中,如果DDL操作挂起时间超过maxTransactionLockRequestTimeoutMillis,等待该操作的挂起事务将被终止。也就是说,maxTransactionLockRequestTimeoutMillis的值必须至少涵盖正在进行的交易和挂起的DDL操作完成所需的时间。

如果有一个事务正在进行,且事务外部的写操作修改了一个文档,而该事务稍后尝试修改的文档,则由于写冲突,事务将被终止。

如果有一个事务正在进行,并且已经锁定了一个文档以修改它,当事务外部的写操作尝试修改同一个文档时,写操作将等待直到事务结束。

事务内的读取操作可能会返回旧数据,这被称为过时读取。事务内的读取操作不能保证看到其他已提交事务或非事务性写入的操作。例如,考虑以下序列

  1. 一个事务正在进行。

  2. 事务外部的写入删除了一个文档。

  3. 由于操作使用了写入操作之前的快照,事务内的读取操作可以读取现在已被删除的文档。

为了避免对单个文档进行事务内的过时读取,您可以使用db.collection.findOneAndUpdate()方法。以下mongosh示例演示了如何使用db.collection.findOneAndUpdate()来获取写入锁并确保您的读取是最新的

1
db.getSiblingDB("hr").employees.insertOne(
{ _id: 1, status: "Active" }
)
2
session = db.getMongo().startSession( { readPreference: { mode: "primary" } } )
3
session.startTransaction( { readConcern: { level: "snapshot" }, writeConcern: { w: "majority" } } )
employeesCollection = session.getDatabase("hr").employees
4
employeeDoc = employeesCollection.findOneAndUpdate(
{ _id: 1, status: "Active" },
{ $set: { lockId: ObjectId() } },
{ returnNewDocument: true }
)

注意,在事务内部,findOneAndUpdate 操作设置了一个新的 lockId 字段。您可以设置 lockId 字段为任何值,只要它修改了文档。通过更新文档,事务可以获取锁。

如果事务外部的操作在提交事务之前尝试修改文档,MongoDB 会向外部操作返回写冲突错误。

5
session.commitTransaction()

提交事务后,MongoDB会释放锁。

注意

如果事务中的任何操作失败,事务将中止,事务中做出的所有数据更改都将被丢弃,这些更改永远不会在集合中可见。

块迁移在某些阶段会获取独占集合锁。

如果进行中的事务对某个集合有锁,并且涉及到该集合的块迁移开始,这些迁移阶段必须等待事务释放集合上的锁,从而影响块迁移的性能。

如果块迁移与事务交错(例如,如果事务在块迁移已经开始之前开始,并且在事务对集合加锁之前迁移完成),则事务在提交时将出现错误并中止。

根据这两个操作的交错方式,一些示例错误包括(错误消息已被缩写)

  • 集群数据放置变更错误...正在为 <namespace> 迁移提交

  • 在集群时间中找不到该块所属的shardId...

在事务提交过程中,外部读取操作可能会尝试读取事务将要修改的相同文档。如果事务写入多个分片,则在跨分片提交尝试期间

  • 使用读取关注点 "snapshot""linearizable" 的外部读取将等待事务的所有写入都可见。

  • 因果一致性会话中的外部读取(包括 afterClusterTime)将等待事务的所有写入都可见。

  • 使用其他读取关注点的外部读取不会等待事务的所有写入都可见,而是读取事务之前的文档版本。

返回

操作