使用猫鼬处理并发修改

Posted

技术标签:

【中文标题】使用猫鼬处理并发修改【英文标题】:Handling concurrent modification w/ mongoose 【发布时间】:2016-05-31 12:30:18 【问题描述】:

我的应用程序中有几个地方使用了 mongoose,我需要在这些地方处理并发修改。

我知道我的所有文档中都有一个版本“__v”。我看到的大部分内容都指向这个博客:

http://aaronheckmann.tumblr.com/post/48943525537/mongoose-v3-part-1-versioning

其中概述了使用 __v 字段来防止文档中的并发数组修改。即:

posts.update( _id: postId, __v: versionNumber 
           ,  $set:  'comments.3.body': updatedText )

mongoose 是否在保存文档时以任何方式自动使用 __v 字段来验证文档?还是您总是必须将其添加到您的查询语句中?

【问题讨论】:

mongoose.set("debug",true) 会为您解答您的问题。并不是说我无论如何都喜欢这个过程。测试__v 的特定值意味着您已经从文档中获得了该信息,并且如果该值/版本不同,您根本无法修改内容。这几乎是不现实的,实际上您的应用程序不应该关心这一点,或者应该以不同的方式应用更新,其中需要“历史”之类的东西。即添加到数组。如果您设计了正确的访问模式,那么您将不会遇到此类问题。 上述代码中的快速示例。你应该从不comments.3.body暗示更新数组中那个索引位置的“body”。相反,您应该写.update( "_id": postId, "comments._id": commentId , "$set": "comments.$.body": updatedText )。这样一来,除了您知道自己真正想要的元素之外,您永远不会更新任何东西,而不是现在可能处于该位置的其他东西。 __v 唯一能帮助的就是避免两个用户同时更改相同的评论。但最好只允许作者进行更改。 【参考方案1】:

查看Mongoose源代码,__v确实只用于arraysVERSION_WHERE是在updating the array path时设置的。

  if ('$push' == op || '$pushAll' == op || '$addToSet' == op) 
    self.$__.version = VERSION_INC;
  
  // now handling $set, $unset
  else if (/\.\d+\.|\.\d+$/.test(data.path)) 
    // subpath of array
    self.$__.version = VERSION_WHERE;
  

根据this answer,

var t = Test();
t.name = 'hi'
t.arr = [1, 2, 3, 4, 5, 6];

 t.save(function (err, result) 
    console.log(result);
    // use Mongoose pull method on the array
    t.arr.pull(3);
    t.save(function(err2, result2) 
        console.log(result2)
    );
);

结果:

  __v: 0,
  name: 'hi',
  _id: 53f59d2a6522edb12114b98c,
  arr: [ 1, 2, 3, 4, 5, 6 ] 
 __v: 1,
  name: 'hi',
  _id: 53f59d2a6522edb12114b98c,
  arr: [ 1, 2, 4, 5, 6 ] 

【讨论】:

以上是关于使用猫鼬处理并发修改的主要内容,如果未能解决你的问题,请参考以下文章

在 Grails (GORM) 中处理并发修改,同时避免过时对象异常

处理大批量的并发

性能测试出现掉并发数现象调优处理

高并发的情况下,如果处理大量请求修改同一样变量,用copy不用加锁。

如何处理高并发量的HTTP请求

数据库 chapter 11 并发控制