MongoDB中的事务支持

19

我是MongoDB的新手。我了解到在这里http://docs.mongodb.org/manual/faq/fundamentals/MongoDB不支持多文档事务

如果我想要原子地保存两个集合(A和B)的数据,那么我不能使用MongoDB,也就是说,如果在B的情况下保存失败,则A仍将具有数据。这不是一个很大的缺点吗?

尽管如此,为什么人们仍然使用MongoDB而不是关系型数据库管理系统?


2
除了太宽泛和主观之外,这个默认答案:因为我们想要并且符合我们的情况?同时也要思考一下:在一个非货币/资源交易应用程序中(就像90%的应用程序一样),有多少行真正依赖于彼此?取而代之的是,您可以为此实现一些被称为最终一致性的东西,从而使它分成几个部分按需提供。当然,这样做也更快速、更容易扩展。 - Sammaye
@Sammaye说得对。这完全取决于您的用例。NoSQL更适用于存储庞大的原始数据并从中获取业务价值。至于事务支持,它不支持。您可能会喜欢这个,但不确定是否已经在市场上使用了http://www.tokutek.com/2013/04/mongodb-multi-statement-transactions-yes-we-can/。 - Ninad
8个回答

15

MongoDB 4.0 现在支持多文档 ACID 事务。

参考资料 请查看


10

更新 MongoDB已经开始支持多文档事务。 https://docs.mongodb.com/manual/core/transactions/

MongoDB不支持多文档事务。

然而,MongoDB提供了单个文档的原子操作。通常这些文档级别的原子操作足以解决需要关系型数据库中ACID事务的问题。

例如,在MongoDB中,您可以在单个文档内嵌入相关数据的嵌套数组或嵌套文档,并在单个原子操作中更新整个文档。关系型数据库可能会使用多个表和行来表示相同类型的数据,这将需要事务支持才能以原子方式更新数据。


3

MongoDB不支持事务,但保存一个文档是原子性的。

因此,最好设计数据库架构,使需要原子性保存的所有数据都放在一个文档中。


1
如果您有两个需要转账的银行账户,将这两个账户都保存在一个文档中是不好的做法。或者,如果您允许所有账户之间进行交易,也不应该将它们全部保存在一个文档中。 - Sammaye
@Sammaye 我可以提供一个比特币的方法作为例子。所以,一个人需要将他的一部分余额转移到另一个人那里。在这种情况下只会创建一个文档 - 一个“交易”(它只是集合的名称,不是 SQL 中的交易)。账户没有与其余额相关的字段,余额始终根据交易列表计算。 - stalk
那么你会有一个交易被两个银行账户读取吗?这听起来非常不安全,使用交易作为余额计数已经够糟糕的了。 - Sammaye
@Sammaye 账户余额始终根据该账户涉及的交易计算,不会读取其他余额。您熟悉比特币吗? - stalk
我不是银行的从业人员,但我熟悉银行使用的软件。我不使用比特币,听到这个消息有点高兴。它可以读取其他余额,在两个账户之间的交易被用于计算两个账户的余额,存在交叉。 - Sammaye
显示剩余2条评论

3

MongoDB不支持关系型数据库中的事务处理。MySQL中存储引擎提供了ACID原则下的事务,这与MongoDB完全不同。

以下是MySQL InnoDB引擎的一些特点:

  • Crash Recovery(崩溃恢复)
  • Double write buffer(双写缓冲区)
  • Auto commit settings(自动提交设置)
  • Isolation Level(隔离级别)

这是MongoDB社区的说法:

MongoDB不支持传统锁定或带有回滚的复杂事务。

MongoDB旨在轻量级、快速且可预测。通过保持事务支持极其简单,MongoDB可以提供更好的性能,特别是对于具有多个数据库服务器进程的分区或副本系统。

事务的目的是确保整个数据库在进行多个操作时保持一致。

但与大多数关系型数据库不同,MongoDB并非设计用于单个主机。它被设计为由多个分片组成的集群,在每个分片中,都是多个服务器的副本集(也可以位于不同的地理位置)。

但如果您仍然希望使事务成为可能:

  • 尝试使用Mongo提供的文档级原子性
  • Mongo中的两阶段提交提供了基本操作的简单事务机制
  • mongomvcc是建立在Mongo之上的,也支持事务,正如他们所说的
  • MySQL和Mongo的混合使用

1

1
这个问题很古老,但对于任何遇到这个页面的人,您可以使用fawn。它是一个npm包,可以解决这个确切的问题。披露:我写了它。
假设你有两个银行账户,一个属于John Smith,另一个属于Broke Individual。你想从John Smith转移20美元给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
虽然您已经在链接中披露了自己的关联,使其不是垃圾邮件,但它仍然是一个“仅链接答案”,这通常会被删除。最好总结回答中的关键点,并提供参考链接。 - Nissa
@JohnnyHK 我已经添加了那个信息。 - e-oj

0

-1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接