Mongoose 异步多次保存冲突

Posted

技术标签:

【中文标题】Mongoose 异步多次保存冲突【英文标题】:Mongoose asynchronous multiple save conflicts 【发布时间】:2014-10-24 01:46:01 【问题描述】:

我在使用 Node/Mongoose/Socket.io 时遇到了一个很大的逻辑问题......假设我有一个服务器模型,它经常在我的应用程序中同时被调用,一些调用涉及更新模型中的数据。

    db.Server.findOne(_id: reference.server).exec(function(error, server) 

        catches.error(error);

        if (server !== null) 

              server.anything = "ahah";

              server.save(function(error)  );

        

    

有时,两个人会同时调用它,而第一个人会 save() 另一个人可能已经有 findOne 的“服务器”并得到了不是最新的“旧对象”并保存()它。

这里的大问题是,当第二个人保存()“服务器”(“旧对象”)时,它实际上会覆盖第一个的更改......你可以想象它会产生的大冲突我的申请。

我曾考虑将所有 save() 方法更改为 update() 以解决问题,但在项目中的某些时候,直接使用 update() 非常棘手,而且不实用。

有没有办法在有人更新 findOne() 调用时“锁定”它?就像当你 findOne() 时你也会说“嘿,我很快就会更新,所以不要让人们现在找到它”(使用 Mongoose,甚至是 MongoDb)

我找了好久没找到答案:(

希望你能理解我的问题;)谢谢!

【问题讨论】:

不。见***.com/questions/11076272/… 那么如何使用 mongo 建立一个有人员的网站? ... 您可以构建架构,以便您可以使用原子update 调用来完成所有文档修改。 Tricky ...如果他们无法管理这种情况,我仍然不明白 Mongoose 的目的是什么,如果有两个人,save() 系统就没用了做类似的动作肯定会在每个网站上发生......感谢您的回复,我会重构一切;) 请记住,Mongoose 确实使用原子 update 调用实现了 save,只有 $sets 文档中您实际更改的字段,所以它不像您那么糟糕重新可能在想。数组操作会给您带来最大的麻烦(这就是 Mongoose 添加versioning 的原因),但无论如何您都需要意识到发生了什么。 【参考方案1】:

正如您在此处所言,这不是处理数据更新的最佳方式。如果您考虑您要求做的事情,它基本上可以归结为:

    从数据库中获取一个对象。 更新代码中的属性。 保存该数据,但不保证有其他修改。

因此,您需要尽可能避免这种模式并遵循常识,即您只需更改当前未设置为该值的现有值。因此,这意味着只需使用 $set 等运算符处理“更新”类型的语句:

db.Server.findOneAndUpdate(
     "_id": refernce.server, "anything":  "$ne": "ahah"  ,
     "$set":  "anything": "ahah"  ,
    function(err,server) 
       if ( server != null ) 

          // then something was actually found and modified
          // so server now is the updated document

       
    
);

这意味着您将丢弃 mongoose 的任何字段验证或其他保存挂钩,但它是一种“原子”形式的更新,因为读取和写入不是单独的操作,这就是您当前的实现方式。

如果您希望实现某种类型的“锁定”,那么类似的方法是您最好的方法。因此,如果您想在文档上设置“状态”以显示某人当前正在编辑它,那么请维护一个字段来执行此操作并将其构建到您的查询中。

对于“阅读”文档并获取要呈现给“编辑”的信息,您可以执行以下操作:

db.Server.findOneAndUpdate(
     "$_id": docId, "locked": false ,
     "$set":  "locked": true  ,
    function(err,document) 

    
);

这意味着当有人“抓取”编辑时,随后的操作将无法这样做,因为他们正在寻找锁定状态为 false 的文档,而它不再是。将您的编辑作为“保存”提交时,同样的原则也适用,正好相反:

db.Server.findOneAndUpdate(
     "$_id": docId, "locked": true ,
     "$set":  "locked": false  ,
    function(err,document) 

    
);

您始终可以执行更高级的操作,例如保存的修订版或通过操作或任何其他形式的处理获得版本号。但一般来说,您应该根据自己的需要自行管理

【讨论】:

最后一个解决方案的唯一问题是,如果另一个人真的需要在“第一个”更改数据之后更改数据,“锁定”状态将中断该过程,并且可以使其他冲突..无论如何都是一个好技巧,我将更改我的代码并尝试以这种方式使用 mongo ;)【参考方案2】:

我刚刚意识到我发布了一篇关于堆栈溢出的类似帖子。你可以在这里找到帖子: How to read/write a document in parallel execution with mongoDB/mongoose

在这篇文章中,有人告诉我在内存中保留一些日期以避免这种行为。这就是我所做的并且效果很好。但如果您使用多进程,则需要找到一种在进程之间共享内存的方法。

【讨论】:

以上是关于Mongoose 异步多次保存冲突的主要内容,如果未能解决你的问题,请参考以下文章

Mongoose 没有找到刚刚保存的数据

Mongoose 在 forEach() 中保存相同的文档

为啥这个 Mongoose 更新不保存到数据库中?

使用 Node.js 和 Mongoose 将查询结果保存到模型/模式的属性

Mongoose 对 promise 拒绝处理

如何在 for 循环中运行 mongoose 方法,因为 mongoose 函数是异步的