Mongoose.js交易

时间:2013-07-03 22:32:59

标签: mongodb transactions

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

以此代码段为例:

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

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

6 个答案:

答案 0 :(得分:9)

如果您确实必须跨多种文档类型(在单独的集合中)拥有事务,那么实现此目的的方法是使用单个表来存储要采取的操作。

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

此插入是原子的,一次完成。然后,您可以在' actions'中执行命令。收集并将它们标记为完成,或在完成时将其删除,在完成后调用原始回调。仅当您的操作处理循环是更新数据库的唯一内容时,此方法才有效。当然,你必须停止使用猫鼬,但是越早你做到这一点就越好。无论如何。

答案 1 :(得分:8)

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

假设您有两个银行账户,一个属于John Smith,另一个属于Broke Individual。您想将20美元从John Smith转移到Broke Individual。假设所有名字和姓氏对都是唯一的,这可能看起来像:

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的工作方式。

它实际上只是教程网站上两阶段提交示例的通用实现:https://docs.mongodb.com/manual/tutorial/perform-two-phase-commits/

答案 2 :(得分:6)

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

您可以使用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.');
        }
      });

显然,回滚本身可能会失败 - 如果这是不可接受的,您可能需要重新构建数据或选择其他数据库。

注意:我在此post详细讨论了这个问题。

答案 3 :(得分:2)

开箱即用的MongoDB不支持事务。但是,它们可以通过Two-phase commit protocol实现。 Here您可以看到有关如何在MongoDB中执行2PC的官方建议。 这种方法非常普遍,可以应用于不同的场景,例如

  • 来自不同馆藏的文件(您的案例)
  • 超过2份文件
  • 对文档的自定义操作

答案 4 :(得分:2)

有一些尝试实施解决方案。在撰写本文时,他们都没有特别活跃。但也许他们工作得很好!

niahmiah's README值得一看。它指出了使用交易的一些缺点,即:

  • 相关馆藏的所有更新和删除都应通过交易处理器,即使您没有故意进行交易。如果你不这样做,那么交易可能不会做他们应该做的事情。
  • 这些更新以及您最初想要的交易将明显慢于正常写入。

niahmiah还表示可能有使用交易的替代方案。

只是提示:如果您不想锁定常用的集合,可以考虑将敏感(事务性)数据与不太敏感的数据分开。

例如,如果您当前在用户集合中持有credits,那么最好将其分成两个集合:用户和< em> UserWallet ,并将credits字段移动到钱包中。然后,您可以自由更新用户文档,而不必担心干扰 UserWallet 集合上的事务。

答案 5 :(得分:2)

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

https://mongoosejs.com/docs/transactions.html