【问题标题】:What's the difference between findAndModify and update in MongoDB?MongoDB 中的 findAndModify 和 update 有什么区别?
【发布时间】:2012-06-02 10:58:42
【问题描述】:

我对 MongoDB 中的 findAndModify 方法有点困惑。它与update 方法相比有什么优势?对我来说,它似乎只是先返回项目然后更新它。但是为什么我需要先退货呢?我阅读了 MongoDB:权威指南,它说它对于操作队列和执行其他需要 get-and-set 样式原子性的操作非常方便。但我不明白它是如何做到这一点的。谁能给我解释一下?

【问题讨论】:

    标签: database mongodb sql-update


    【解决方案1】:

    如果您获取一个项目然后更新它,那么这两个步骤之间可能会有另一个线程进行更新。如果您先更新一个项目,然后再获取它,则中间可能会有另一个更新,您将得到一个与您更新的项目不同的项目。

    “原子”执行意味着您可以保证获得与您正在更新的完全相同的项目 - 即在这两者之间不会发生其他操作。

    【讨论】:

    • 我还是有点困惑。 findAndModify如何保证没有其他更新操作干扰?
    • @chaonextdoor findAndModify 在开始操作时获取数据库锁,以便在运行时没有其他操作可以处理。当它完成操作时,它会释放锁。
    • findAndModify 直到请求的修改部分才真正获得锁。因此,多个进程可以更新同一条记录。
    • @MarkUnsworth 使用 10gen 打开一个支持案例 - 如果使用 findAndModify 锁定存在错误,我可以保证您的工程师希望尽快修复它。如果是这种情况,虽然我们会看到很多人报告这种行为,但 findAndModify 的工作原理几乎适用于所有使用它的人 - 似乎它不是归结为客户端的逻辑或实现错误的情况,但当然在复杂的软件中总会有错误。
    • @AsyaKamsky 我正在考虑与你争论,但后来意识到你是对的,所以现在我想我应该道歉。对不起!
    【解决方案2】:

    一类有用的用例是计数器和类似用例。例如,看一下这段代码(MongoDB 测试之一): find_and_modify4.js.

    因此,使用 findAndModify 您递增计数器并使其递增 一步价值。比较:如果您 (A) 分两步执行此操作并且 其他人 (B) 在您的步骤之间执行相同的操作,然后 A 和 B 可能 获得相同的最后一个计数器值,而不是两个不同的(只是可能问题的一个示例)。

    【讨论】:

      【解决方案3】:

      findAndModify 返回文档,更新不返回。

      如果我正确理解 Dwight Merriman(mongoDB 的原作者之一),使用 update 修改单个文档 ie("multi":false} 也是原子的。目前,它也应该比进行等效更新更快使用findAndModify

      【讨论】:

        【解决方案4】:

        来自MongoDB docs(已添加重点):

        • 默认情况下,这两个操作都会修改一个文档。但是,带有 multi 选项的 update() 方法可以修改多个文档

        • 如果多个文档符合更新条件,对于 findAndModify(),您可以指定一个排序,以提供对更新哪个文档的控制措施。 使用 update() 方法的默认行为,当多个文档匹配时,您无法指定要更新哪个单个文档。

        • 默认情况下,findAndModify() 方法返回文档预修改版本。要获取更新的文档,请使用新选项。 update() 方法返回一个包含操作状态的 WriteResult 对象。要返回更新后的文档,请使用 find() 方法。但是,其他更新可能在您的更新和文档检索之间修改了文档。此外,如果更新只修改了单个文档但匹配了多个文档,您将需要使用额外的逻辑来识别更新的文档。

        • 在 MongoDB 3.2 之前,您无法为 findAndModify() 指定写入关注点以覆盖默认的写入关注点,而自 MongoDB 2.6 起,您可以为 update() 方法指定写入关注点

        修改单个文档时,findAndModify() 和 update() 方法都会自动更新文档。

        【讨论】:

        • 你总是可以指定写关注来更新操作。此外,从 3.2 开始(技术上为 3.1.1),您也可以指定将关注点写入 findAndModify。 jira.mongodb.org/browse/SERVER-6558
        • 我发现在 MongDB 3.6 中,虽然文档状态 findAndModify() 默认只修改一个文档,update() 可以更新一个或多个文档,但当我使用 arrayFilters、@987654326 @ 更新所有匹配。也许这是一个错误??
        • 没有错误 - arrayFilters 允许您更新多个数组元素,但它们仍然在单个文档中。
        【解决方案5】:

        我们将 findAndModify() 用于计数器操作(inc 或 dec)和其他单字段变异情况。将我们的应用程序从 Couchbase 迁移到 MongoDB,我发现这个 API 可以替换执行 GetAndlock() 的代码,在本地修改内容,replace() 保存并再次 Get() 以获取更新的文档。对于 mongoDB,我只使用了这个返回更新文档的 API。

        【讨论】:

          【解决方案6】:

          这是一个老问题,但很重要,其他答案只是让我提出更多问题,直到我意识到:这两种方法非常相似,在很多情况下您都可以使用。

          • findAndModifyupdate 在单个请求中执行原子更改,例如递增计数器;事实上,<query><update> 参数在很大程度上是相同的
          • 对于这两种方法,当服务器找到与查询匹配的文档时,原子更改直接发生在该文档上,即在服务器确认查询有效并应用更新的几分之一毫秒内对该文档进行内部写锁定

          没有用户可以获得的系统级写锁或信号量。句号。 MongoDB 故意不让签出文档然后对其进行更改然后将其写回变得容易,同时以某种方式阻止其他人同时更改该文档。 (虽然开发人员可能认为他们想要这样,但在可扩展性和并发性方面,它通常是一种反模式......作为一个简单的例子,想象一个客户端获取写锁,然后在持有它时被杀死。如果你真的想要一个写锁,你可以在文档中做一个,用原子变化来比较设置,然后确定你自己的恢复过程来处理废弃的锁等。但是如果你这样做的话要小心。)

          据我所知,这些方法主要有两种不同

          • 如果您在更新时想要一份文档副本:只有findAndModify 允许这样做,如前所述,在更新后返回原始(默认)或new 记录;使用update,您只会得到WriteResult,而不是文档,当然,在之前或之后立即阅读文档并不能保护您免受另一个进程的影响,同时在您的阅读和更新之间更改记录
          • 如果可能有多个匹配的文档findAndModify 只更改一个,并允许您自定义sort 以指示应该更改哪个; update 可以用multi 全部改,虽然默认只有一个,但是不让你说是哪一个

          因此,HungryCoder 所说的是有道理的,即 update 效率更高,您可以忍受它的限制(例如,您不需要阅读文档;或者当然,如果您是更改多条记录)。但是对于许多原子更新,您确实需要该文档,并且findAndModify 在那里是必需的。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2016-08-11
            • 2019-02-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2011-01-01
            • 2016-01-06
            相关资源
            最近更新 更多