mongodb Mongoose.js 事务

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17459167/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 13:18:29  来源:igfitidea点击:

Mongoose.js transactions

mongodbtransactions

提问by Zebra Propulsion Lab

I know MongoDB doesn't support transactions as relational databases do, but I still wonder how to achieve atomicity for several operations. Hunting around the web, I see people mentioning Transactions without Transactions. Reading through the slides, I am still not clear how to implement that with Mongoose.js.

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

Take this code snippet for example:

以这个代码片段为例:

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

How do I implement callback1 and callback2 so that they either succeed together or fail together?

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

回答by Will Shaver

If you really must have transactions across multiple documents types (in separate collections) the means to achieve this is with a single table that stores actions to take.

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

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

This insert is atomic, and all done at once. You then can perform the commands in the 'actions' collection and mark them as complete or delete them as you complete them, calling your original callback when they are all completed. This only works if your actions processing loop is the only thing updating the db. Of course you'll have to stop using mongoose, but the sooner you do that the better you'll be anyway.

这个插入是原子的,并且一次完成。然后,您可以执行“动作”集合中的命令并将它们标记为完成或在完成时将其删除,当它们全部完成时调用您的原始回调。这仅在您的操作处理循环是唯一更新数据库的情况下才有效。当然,您必须停止使用猫鼬,但您越早这样做越好。

回答by oj_1boss

This question is quite old but for anyone who stumbles upon this page, you could use fawn. It's an npm package that solves this exact problem. Disclosure: I wrote it

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

Say you have two bank accounts, one belongs to John Smith and the other belongs to Broke Individual. You would like to transfer $20 from John Smith to Broke Individual. Assuming all first name and last name pairs are unique, this might look like:

假设您有两个银行账户,一个属于 John Smith,另一个属于 Broke Individual。您想从 John Smith 向 Broke Individual 转账 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);
  });

Caveat: tasks are currently not isolated(working on that) so, technically, it's possible for two tasks to retrieve and edit the same document just because that's how MongoDB works.

警告:任务目前不是孤立的(正在处理),因此,从技术上讲,两个任务可以检索和编辑同一个文档,因为这就是 MongoDB 的工作方式。

It's really just a generic implementation of the two phase commit example on the tutorial site: https://docs.mongodb.com/manual/tutorial/perform-two-phase-commits/

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

回答by jramoyo

You can simulate a transaction by manually rolling-back the changes whenever an error occurs. In your above example, simply remove the saved document if the other one fails.

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

You can do this easily using Async:

您可以使用以下方法轻松完成此操作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.');
        }
      });

Obviously, the rollback itself could fail -- if this is a not acceptable, you probably need to restructure your data or choose a different database.

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

Note: I discussed this in detail on this post.

注意:我在这篇文章中详细讨论了这一点。

回答by Jo?o Otero

Transactions are now supported in Mongoose >= 5.2.0 and MongoDB >= 4.0.0 (with replica sets)

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

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

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

回答by Sergiy Yeskov

Transactions are not supported in MongoDB out of the box. However they are implementable with Two-phase commit protocol. Hereyou can see official recommendation on how to do 2PCs in MongoDB. This approach is quite universal and can be applied to different scenarios such as

开箱即用的 MongoDB 不支持事务。然而,它们可以通过两阶段提交协议来实现。在这里你可以看到关于如何在 MongoDB 中做 2PCs 的官方建议。这种方法非常通用,可以应用于不同的场景,例如

  • documents from different collections (your case)
  • more than 2 documents
  • custom actions on documents
  • 来自不同集合的文件(你的情况)
  • 2个以上的文件
  • 对文档的自定义操作

回答by joeytwiddle

There have been a few attempts to implement solutions. None of them are particularly active at the time of writing. But maybe they work just fine!

已经进行了一些尝试来实施解决方案。在撰写本文时,他们都不是特别活跃。但也许他们工作得很好!

niahmiah's READMEis worth looking at. It notes some disadvantages of using transactions, namely:

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

  • All updates and removals for the relevant collections should go through the transaction processor, even when you weren't intentionally doing a transaction. If you don't do this, then transactions may not do what they are supposed to do.
  • Those updates, as well as the transactions you initially wanted, will be significantly slower than normal writes.
  • 相关集合的所有更新和删除都应通过事务处理器,即使您并非有意进行事务。如果您不这样做,那么交易可能不会做他们应该做的事情。
  • 这些更新以及您最初想要的事务将比正常写入慢得多。

niahmiah also suggests that there may be alternatives to using transactions.

niahmiah 还建议使用交易可能有其他选择。



Just a tip: If you don't like the idea of locking up your frequently used collections, you could consider separating the sensitive (transactional) data from the less sensitive data.

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

For example, if you are currently holding creditsin the Usercollection, then you would be better off separating it into two collections: Userand UserWallet, and moving the creditsfield into the wallets. Then you can freely update Userdocuments without fear of interfering with transactions on the UserWalletcollections.

例如,如果你目前持有credits的在用户集合,然后你就可以关闭它分成两个集合更好:用户UserWallet,以及移动credits领域进入钱包。然后您可以自由更新用户文档,而不必担心干扰UserWallet集合上的交易。