MongoDB中findAndModify和更新有什么区别?

cha*_*oor 167 mongodb

我对findAndModifyMongoDB中的方法感到有点困惑.它比这个update方法有什么优势?对我来说,它似乎只是首先返回项目,然后更新它.但为什么我需要先退回物品呢?我阅读了MongoDB:权威指南,它说它可以方便地操作队列和执行需要获取和设置样式原子性的其他操作.但我不明白它是如何实现这一目标的.有人可以向我解释一下吗?

Asy*_*sky 146

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

"原子地"执行此操作意味着您可以保证返回正在更新的完全相同的项目 - 即之间不会发生其他操作.

  • @chaonextdoor findAndModify在启动操作时获取对数据库的锁定,以便在其运行时不会处理任何其他操作.完成操作后,它会释放锁定. (76认同)
  • 我还是有点困惑.`findAndModify`如何保证没有其他更新操作干扰它? (6认同)
  • @AsyaKamsky我正在考虑和你争吵然后意识到你是对的,所以现在我想我应该道歉.抱歉! (4认同)
  • 在修改请求部分之前,findAndModify实际上不会获得锁定.因此,多个进程可以更新同一记录. (3认同)
  • @MarkUnsworth用10gen打开一个支持案例 - 如果使用findAndModify锁定存在错误,我可以保证工程师会尽快修复它.如果是这种情况,虽然我们会看到很多人报告这种行为,但findAndModify按照几乎所有使用它的人的设计工作 - 看起来它似乎没有归结为客户端的逻辑或实现错误,但当然在复杂的软件中总会有一个bug. (3认同)
  • 大声笑没有后顾之忧:) (3认同)
  • @MarkUnsworth不正确.请参阅https://github.com/mongodb/mongo/blob/master/src/mongo/db/commands/find_and_modify.cpp#L122获取writeLock的操作持续时间. (2认同)
  • 不确定是否曾经提交过票证,因为 https://github.com/mongodb/mongo/blob/master/src/mongo/db/commands/find_and_modify.cpp#L137 仍然读取 `Lock::DBWrite` - 它获得只有一个写锁。 (2认同)
  • 这不能回答问题。我认为其他答案更有用(即它们解释了 findAndModify 和 update 之间的区别,而不是简单地解释原子性) (2认同)

小智 51

findAndModify 返回文档,更新不.

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


Tam*_*lyn 28

来自MongoDB文档(重点补充):

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

  • 如果多个文档与更新条件匹配,则对于findAndModify(),您可以指定排序以提供对要更新的文档的某种控制措施.使用update()方法的默认行为,您无法指定在多个文档匹配时要更新的单个文档.

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

  • 您不能指定findAndModify()的写入问题来覆盖默认的写入问题,而从MongoDB 2.6开始,您可以指定 update()方法的写入问题.

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


Rom*_*min 9

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

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


Par*_*udy 9

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

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

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

据我所知,这些方法有两个主要区别

  • 如果您在更新时想要文档的副本:仅findAndModify允许此操作new,如前所述,在更新后返回原始(默认)或记录;update您只得到 a ,WriteResult而不是文档,当然,在之前或之后立即阅读文档并不能防止另一个进程在您的读取和更新之间更改记录
  • 如果可能有多个匹配文档findAndModify仅更改一个,并允许您自定义sort来指示应更改哪一个;update可以更改全部,multi虽然默认只有一个,但不让你说出是哪一个

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