Mongodb Mongoose.js事务

Mongodb Mongoose.js事务,mongodb,transactions,Mongodb,Transactions,我知道MongoDB不像关系数据库那样支持事务,但我仍然想知道如何实现几个操作的原子性。在网上搜寻,我看到有人提到。通过阅读幻灯片,我仍然不清楚如何使用Mongoose.js实现这一点 以此代码段为例: player.save(callback1); story.save(callback2); 如何实现callback1和callback2,以便它们一起成功或一起失败?如果您确实必须跨多个文档类型(在单独的集合中)进行事务处理,则实现这一点的方法是使用一个存储要执行的操作的表 db.acti

我知道MongoDB不像关系数据库那样支持事务,但我仍然想知道如何实现几个操作的原子性。在网上搜寻,我看到有人提到。通过阅读幻灯片,我仍然不清楚如何使用Mongoose.js实现这一点

以此代码段为例:

player.save(callback1);
story.save(callback2);

如何实现callback1和callback2,以便它们一起成功或一起失败?

如果您确实必须跨多个文档类型(在单独的集合中)进行事务处理,则实现这一点的方法是使用一个存储要执行的操作的表

db.actions.insert(
{ actions: [{collection: 'players', _id: 'p1', update: {$set : {name : 'bob'} } },
            {collection: 'stories', _id: 's1', update: {$set : {location: 'library'} } }], completed: false }, callback);

此插入是原子的,并且所有操作都一次完成。然后,您可以执行“actions”集合中的命令,并将它们标记为已完成,或在完成时删除它们,在它们全部完成时调用原始回调。只有当操作处理循环是更新数据库的唯一内容时,这才有效。当然,您必须停止使用mongoose,但越快停止使用,您就越好。

您可以通过在出现错误时手动回滚更改来模拟事务。在上面的示例中,如果另一个文档失败,只需删除保存的文档即可

您可以使用
Async
轻松完成此操作:

    function rollback (doc, cb) {
      doc.remove(cb);
    }

    async.parallel([
          player.save.bind(player),
          story.save.bind(story),
      ],
      function (err, results) {
        if (err) {
          async.each(results, rollback, function () {
            console.log('Rollback done.');
          });
        } else {
          console.log('Done.');
        }
      });
显然,回滚本身可能会失败——如果这是一个不可接受的错误,您可能需要重新构造数据或选择其他数据库


注意:我对此进行了详细讨论。

MongoDB开箱即用不支持事务。但是,它们是可以通过以下方式实现的。您可以看到关于如何在MongoDB中实现2PCs的官方建议。 这种方法非常通用,可以应用于不同的场景,例如

  • 来自不同集合的文档(您的案例)
  • 超过2份文件
  • 文档上的自定义操作

有几次尝试实施解决方案。他们在写作时都不是特别活跃。但也许他们工作得很好

  • 缺少文档,闲置1年

  • 记录在案,闲置2年

  • 提供事务支持,但实际上远不止于此,6个月闲置

值得一看。它指出了使用事务的一些缺点,即:

  • 相关集合的所有更新和删除都应通过事务处理器进行,即使您不是有意进行事务处理。如果您不这样做,那么事务可能不会做它们应该做的事情
  • 这些更新以及您最初想要的事务将比正常写入慢得多
niahmiah还建议,除了使用事务,可能还有其他选择


只是一个提示:如果你不喜欢锁定频繁使用的集合,你可以考虑从敏感数据中分离敏感的事务数据。


例如,如果您当前在用户集合中持有
积分
,则最好将其分为两个集合:User和UserWallet,并将
积分
字段移动到wallet中。然后,您可以自由更新用户文档,而无需担心会干扰UserWallet collections上的交易。

这个问题已经很老了,但对于偶然发现此页面的任何人,您都可以使用。这是一个npm包,解决了这个确切的问题。披露:是我写的

假设你有两个银行账户,一个属于约翰·史密斯,另一个属于破产个人。你想把约翰·史密斯的20美元转帐给破产的个人。假设所有名字和姓氏对都是唯一的,这可能看起来像:

var Fawn = require("fawn");
var task = Fawn.Task()

//assuming "Accounts" is the Accounts collection 
task.update("Accounts", {firstName: "John", lastName: "Smith"}, {$inc: {balance: -20}})
  .update("Accounts", {firstName: "Broke", lastName: "Individual"}, {$inc: {balance: 20}})
  .run()
  .then(function(){
    //update is complete 
  })
  .catch(function(err){
    // Everything has been rolled back. 

    //log the error which caused the failure 
    console.log(err);
  });
警告: 任务目前不是孤立的(正在处理),因此,从技术上讲,两个任务可以检索和编辑同一个文档,因为MongoDB就是这样工作的


这实际上只是教程站点上两阶段提交示例的一般实现:

事务现在在Mongoose>=5.2.0和MongoDB>=4.0.0(带有副本集)中受支持


这个问题与mongoose或node.js无关。如果您需要事务,请选择其他数据库平台,此链接可能会帮助您。MongoDB没有事务,甚至上面来自@Jean PhilippeLeclerc的链接也只提供类似事务的行为。而且,它肯定不像事务数据库那么容易实现。MongoDB 4.0的可能副本现在有事务支持。(发布日期:2018-07)自从MongoDB 4.0.0(和Mongoose 5.2.0)以来,MongoDB中现在支持事务:如何使用node.js中的Mongoose插件来使用它当Will说“你必须停止使用Mongoose”时,我想他的意思是你将不再能够在这些集合中自由使用它。您可以继续在从未用于事务的其他集合上使用mongoose,您的操作处理循环甚至可以使用mongoose完成其工作,但您应该只通过该队列处理系统处理受保护的集合。这可能是一种很好的方法,但是,重新发明RDBMs比回滚失败更令人沮丧的是,在这个过程中,服务器可能会在某个时候崩溃。对于生产环境,重要的是考虑到服务器在第二文档失败后可能崩溃,但在基于失败的第二文档应用回滚过程之前。处理这个案子特别棘手!请注意,当其他请求在数据库中执行相同的操作时,这并不能阻止争用条件。但是,如果您想将第一个事务的结果用于第二个事务,您具体如何才能做到这一点?示例:let currLog=await Log.findOne({“id”:logID});让newUser=wait User.save(有效负载);让updatedLog=wait currLog.users.push(newUser.\u id);可以用小鹿吗