在MongoDB中,写入操作在单文档级别上具有 原子性,即使修改多个值也是如此。对于并行更新,每个命令确保查询条件仍然匹配。
为防止并发更新期间发生冲突,请在更新过滤中包含预期的当前值。
用例
考虑包含此文档的集合:
db.games.insertOne( { _id: 1, score: 80 } )
这些更新操作会同时发生:
// Update A db.games.updateOne( { score: 80 }, { $set: { score: 90 } } ) // Update B db.games.updateOne( { score: 80 }, { $set: { score: 100 } } )
一项更新将 score 设置为 90 或 100。随后,第二次更新无法匹配 { score: 80 },因此无法运行。
警告
在并发更新期间,筛选不更新的字段可能会导致意外结果。考虑以下操作:
// Update A db.games.updateOne( { _id: 1 }, { $set: { score: 90 } } ) // Update B db.games.updateOne( { _id: 1 }, { $set: { score: 100 } } )
两次更新均与 { _id: 1 } 匹配,因此均运行。第二次更新将覆盖第一次。第一个客户端不会收到有关其更新丢失的警告。
为避免筛选未更新字段时发生冲突,请使用$inc 。
示例,考虑以下并发更新操作:
// Update A db.games.updateOne( { _id: 1 }, { $inc: { score: 10 } } ) // Update B db.games.updateOne( { _id: 1 }, { $inc: { score: 20 } } )
两次更新都与 { _id: 1 } 匹配。由于它们会递增而不是设立值,因此不会相互覆盖。最后的 score 是 110。
详情
本节介绍多文档事务的其他详细信息。
当单个写操作(例如 db.collection.updateMany())修改了多份文档,则每份文档的修改都是原子性的,但整个操作不是原子性的。
在执行多文档写入操作时,无论是通过单次写入操作还是多次写入操作,其他操作都可能会交错进行。
对于需要对多个文档(在单个或多个集合中)原子性读取和写入的情况,MongoDB 支持分布式事务,包括副本集和分片集群上的事务。
有关详细信息,请参阅事务。
重要
在大多数情况下,与单文档写入操作相比,分布式事务会产生更高的性能成本,并且分布式事务的可用性不应取代有效的模式设计。在许多情况下,非规范化数据模型(嵌入式文档和数组)仍然是数据和使用案例的最佳选择。换言之,对于许多场景,适当的数据建模将最大限度地减少对分布式事务的需求。
有关其他事务使用注意事项(如运行时间限制和 oplog 大小限制),另请参阅生产注意事项。