v4之前的MongoDB不符合ACID的含义是什么?


226

我不是数据库专家,也没有正式的计算机科学背景,所以请耐心等待。我想知道如果您使用v4之前的旧MongoDB版本(不兼容ACID)会在现实世界中产生负面影响。这适用于任何不符合ACID的数据库。

我知道MongoDB可以执行Atomic Operations,但是它们不“支持传统的锁定和复杂事务”,主要是出于性能方面的考虑。我还了解数据库事务的重要性,以及当您的数据库用于银行时的示例,并且您正在更新所有都需要同步的几条记录,如果存在以下情况,您希望事务恢复到初始状态:停电,因此信用等于购买等。

但是,当我开始谈论有关MongoDB的话题时,我们当中那些不了解数据库实际实现方式的技术细节的人开始抛出如下语句:

MongoDB比MySQL和Postgres快得多,但是有一个很小的机会,例如“百万分之一”,“不会正确保存”。

“无法正确保存”部分指的是这种理解:如果在写MongoDB的那一刻就出现断电,就有可能出现一条特定记录(例如,您正在跟踪具有10个属性的文档中的综合浏览量每个文档),其中一个文档仅保存了5个属性……这意味着,随着时间的流逝,您的综合浏览量计数器将“略微”关闭。您永远不会知道多少,您会知道它们的正确率是99.999%,但不是100%。这是因为,除非您专门将此操作指定mongodb原子操作,否则不能保证该操作是原子操作

所以我的问题是,什么时候以及为什么MongoDB可能无法“正确保存”的正确解释是什么?它不满足ACID的哪些部分,在什么情况下,以及您怎么知道何时丢失0.001%的数据?无法以某种方式解决此问题吗?如果没有,这似乎意味着您不应该users在MongoDB中存储诸如表之类的内容,因为记录可能无法保存。但是话又说回来,那1 / 1,000,000用户可能只需要“尝试再次注册”,不是吗?

我只是在寻找与ACID不兼容的数据库(例如MongoDB)何时/为什么会发生负面事件的列表,并且理想情况下是否存在标准的解决方法(例如运行后台作业以清理数据,或仅使用SQL进行此操作等)。 。

Answers:


133

您在MongoDB中失去的一件事是多集合(表)事务。MongoDB中的原子修饰符只能对单个文档起作用。

如果您需要从库存中删除某项并将其同时添加到某人的订单中,则不能。除非这两个东西(库存和订单)存在于同一文档中(它们可能不存在)。

我在正在处理的应用程序中遇到了同样的问题,并且有两种可能的解决方案可供选择:

1)尽最大可能组织文档,并尽最大可能使用原子修饰符,对于其余位,请使用后台进程来清理可能不同步的记录。例如,我从库存中删除项目,然后使用原子修饰符将它们添加到同一文档的reservedInventory数组中。

这使我始终知道库存中没有可用的物料(因为它们是由客户保留的)。当客户结帐时,我然后从reservedInventory中删除项目。它不是标准交易,并且由于客户可以放弃购物车,因此我需要一些后台流程来查找废弃的购物车并将保留的库存移回可用的库存池。

这显然不理想,但它是mongodb不能完美满足需求的大型应用程序的唯一部分。另外,到目前为止,它还可以完美地工作。在许多情况下这可能是不可能的,但是由于我使用的文档结构非常合适。

2)与MongoDB结合使用事务数据库。通常,使用MySQL为绝对需要的事物提供事务,同时让MongoDB(或任何其他NoSQL)尽其所能。

如果长期以来我的#1解决方案不起作用,我将进一步研究将MongoDB与MySQL结合使用,但目前#1非常适合我的需求。


27
MongoDB中的原子修饰符只能对单个集合起作用 ” =>我认为您的意思是“针对单个文档 ”。
assylias 2013年

2
优秀的信息,通常是一个很好的答案,但建议使用MySQL。
Doug Molineux 2014年

with您在MongoDB中失去的一件事是多集合(表)事务。MongoDB中的原子修饰符只能针对mongo doc中的单个文档mondocs.mongodb.com/v3.2/core/write-operations-atomicity):“在MongoDB中,写操作在单个级别上是原子的即使该操作修改了单个文档中的多个嵌入式文档,也是如此。”
yoav.str

5
缺少多文档ACID交易不再是这种情况。MongoDB宣布他们将在v4.0中推出。参见mongodb.com/blog/post/multi-document-transactions-in-mongodb
Grigori Melnik,

1
到目前为止,由于MongoDB 4.0是ACID兼容的mongodb.com/transactions,具有多文档事务。看看mongodb.com/blog/post/…–
Ratah,

134

MongoDB不兼容ACID实际上是不正确的。相反,MongoDB 在文档级别是ACID编译器。

对单个文档的任何更新是

  • 原子:它要么完全完成,要么没有完成
  • 一致:没有读者会看到“部分应用”更新
  • 孤立:再次,没有读者会看到“脏”读物
  • 耐用:(具有适当的写意)

MongoDB没有的是事务 -即可以回滚并且符合ACID的多文档更新。

请注意,您可以使用两阶段提交在与ACID兼容的更新之上构建单个文档的事务。


3
请注意,两阶段提交的事务不符合ACID。由于某种原因,我推断出相反的意思,直到我跟随链接。
贾斯汀C

1
不管写关注配置如何,都存在有关分布式MongoDB在文档级别的持久性的问题。开源工具Jepsen发现,即使存在MAJORITY写问题,数据也可能在网络分区中丢失。参见此处的文章:aphyr.com/posts/284-call-me-maybe-mongodb
jrullmann 2014年

9
在单个文档级别具有ACID(在某种程度上等效于RDBMS中的单个记录)在许多情况下没有用。事务术语与单个表无关,您甚至可以采用两阶段提交的机制,并涉及多个XAResource,因此将单个文档称为ACID兼容是一个问题,恕我直言。
Yair Zaslavsky

5
同意Yair。“在文档级别符合ACID”不是卖点。它基本上只是意味着“不符合ACID”。ACID绝不是要“仅一行/文档/实体”。这是为了使您的数据在整个数据库中保持一致。
joshua.paling

34

一个很好的解释包含在“星巴克不使用两相提交”中

这与NoSQL数据库无关,但是它确实说明了有时您可以承受丢失事务或使数据库暂时处于不一致状态的观点。

我认为这不是需要“修复”的东西。解决方法是使用符合ACID的关系数据库。当NoSQL的行为符合您的应用程序要求时,您可以选择它。


1
像任何类比一样,它也有其局限性。在软件中,很容易创建新的Array [Cashiers]并让它们分别处理同步事务,而在现实世界中,这样做的成本将是非常高的。
HRJ 2012年

16

我认为其他人已经给出了很好的答案。但是我想补充一点,有ACID NOSQL数据库(例如http://ravendb.net/)。因此,这不仅是决策NOSQL-没有ACID还是与ACID关系...。


1
谢谢@subGate。有谁可以分享ravenDB的经验,并且确实满足要求吗?
尼·彭加斯

12

“无法正确保存”可能意味着:

  1. 默认情况下,MongoDB不会立即将更改保存到驱动器。因此,您可能会告诉用户“更新成功”,发生断电并且更新丢失。MongoDB提供了用于控制更新“持久性”级别的选项。它可以等待其他副本接收此更新(在内存中),等待对本地日志文件的写入等。

  2. 对多个集合,甚至同一集合中的多个文档,都没有简单的“原子”更新。在大多数情况下,这不是问题,因为可以通过“ 两阶段提交”来规避它,也可以通过重组架构来对单个文档进行更新。看到以下问题:文档数据库:冗余数据,引用等(专门用于MongoDB)


10

从MongoDB v4.0开始,将支持多文档ACID事务。通过快照隔离,事务将提供全局一致的数据视图,并强制执行全部或全部执行以维护数据完整性。

他们感觉像是来自关系世界的交易,例如:

with client.start_session() as s:
    s.start_transaction()
    try:
        collection.insert_one(doc1, session=s)
        collection.insert_one(doc2, session=s)
        s.commit_transaction()
    except Exception:
        s.abort_transaction()

参见https://www.mongodb.com/blog/post/multi-document-transactions-in-mongodb



5

请阅读有关ACID属性的详细信息

同样在MongoDB文档中,您可以找到问题和答案

MongoDB不兼容ACID。请阅读以下内容,以了解有关ACID的规定。

  1. MongoDB A仅在文档级别是tomic。它不符合我们从关系数据库系统中了解到的原子定义,特别是上面的链接。从这个意义上说,MongoDB不符合ACID中的A。
  2. C默认情况下,MongoDB是启用的。 但是,您可以从副本集中的辅助服务器读取。在这种情况下,您只能具有最终的一致性。如果您不介意读取稍微过时的数据,这将很有用。
  3. MongoDB不保证I关联(同样根据上述定义):
  1. 对于具有多个并发读取器和写入器的系统,MongoDB将允许客户端在写入操作返回之前读取写入操作的结果。
  2. 如果mongod在日记提交之前终止,即使写操作成功返回,查询可能已读取mongod重新启动后将不存在的数据。

但是,MongoDB会单独修改每个文档(用于插入和更新)。仅在单据级别,而不在多单据交易中。

  1. 关于D可用性-您可以使用该write concern选项配置此行为,但不确定。也许有人更了解。

我相信正在进行一些研究,以将NoSQL推向ACID约束或类似约束。这是一个挑战,因为NoSQL数据库通常更快并且ACID约束会大大降低性能。


4

原子修改单个集合的工作的唯一原因是因为mongodb开发人员最近用集合范围的写锁交换了数据库锁。决定在这里增加并发值得权衡。从根本上讲,mongodb是一个内存映射文件:他们已将缓冲池管理委托给了计算机的vm子系统。因为它始终在内存中,所以他们可以摆脱非常粗暴的锁定:您将在按住它的同时执行仅内存中操作,这将非常快。这与传统的数据库系统有很大的不同,传统的数据库系统有时会在按住页面锁或行锁的同时被迫执行I / O。


您能否解释一下为什么这会增加并发性?抱歉,如果我在这里没有明显的地方。
batbrat 2014年

@batbrat:考虑两个试图同时写入同一数据库中不同集合的客户端。使用数据库锁,其中一个客户端将必须等待另一个客户端完成才能进行写操作。使用收集锁,两个客户端都可以同时写入。这就是并发性的提高。当然,如果两个客户端都尝试写入同一集合,则必须等待。
jrullmann 2014年

2

“在MongoDB中,对单个文档的操作是原子的” – 这就是过去的事情

在新版本的MongoDB 4.0中,您可以:

但是,对于需要原子性来更新多个文档或读取多个文档之间保持一致性的情况,MongoDB提供了针对副本集执行多文档事务的功能。多文档事务可用于多个操作,集合,数据库和文档。多文档交易提供了“全有或全无”的主张。提交事务时,将保存在事务中进行的所有数据更改。如果事务中的任何操作失败,事务将中止,并且在事务中进行的所有数据更改都将被丢弃,而不会变得可见。在提交事务之前,在事务外部看不到该事务中的任何写操作。

尽管有对一些限制如何什么操作都不能进行。

检查Mongo文档。 https://docs.mongodb.com/master/core/transactions/


1

如果您的存储支持每个键的线性化并进行比较和设置(对于MongoDB是正确的),则可以在客户端实现原子多键更新(可序列化事务)。这种方法已在Google的PercolatorCockroachDB中使用,但没有什么可以阻止您在MongoDB中使用它。

我已经创建了此类交易的逐步可视化。希望它能帮助您理解它们。

如果您对读取的提交隔离级别没问题,那么可以看看Peter Bailis的RAMP事务。它们也可以在客户端针对MongoDB实施。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.