在v4真正意味着什么之前,MongoDB不符合ACID标准?

时间:2011-08-22 15:35:26

标签: sql mongodb acid database nosql

我不是数据库专家,没有正式的计算机科学背景,所以请耐心等待。我想知道如果您使用不符合MongoDB version prior to v4的旧ACID,可能会发生的真实世界负面事件。这适用于任何ACID不合规数据库。

我知道MongoDB可以执行Atomic Operations,但它们并不“支持传统锁定和复杂事务”,主要是出于性能原因。我也理解数据库事务的重要性,以及数据库何时用于银行的示例,并且您正在更新所有需要同步的多个记录,您希望事务恢复到初始状态(如果有)停电,所以信贷等于购买等。

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

  

MongoDB比MySQL和Postgres更快,但是有一个很小的机会,比如百万分之一,它“无法正确保存”。

“无法正确保存”部分指的是这样一种理解:如果您在写入MongoDB的那一刻发生了断电,那么就有机会获得特定记录(比如说您正在跟踪文档中的网页浏览量)每个10个属性),其中一个文件只保存了5个属性...这意味着随着时间的推移,你的网页浏览计数器将“略微”关闭。你永远不会知道多少,你知道他们将是99.999%正确,但不是100%。这是因为,除非您特意将其设为mongodb atomic operation,否则不保证操作是原子操作。

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

我只是在寻找可能列出的时间/为什么会出现负面事情的列表,如AliD不合规数据库(如MongoDB),理想情况下是否有标准的解决方法(比如运行后台作业来清理数据,或者只使用SQL,等)。

10 个答案:

答案 0 :(得分:130)

MongoDB不符合ACID标准实际上是不正确的。相反,MongoDB在文档级别是

对单个文档的任何更新都是

  • Atomic:它完全完成或不完整
  • 一致:没有读者会看到“部分应用”更新
  • 隔离:再次,没有读者会看到“脏”的阅读
  • 耐用:(有适当的写作关注)

MongoDB没有的是 事务 - 也就是说,可以回滚且符合ACID的多文档更新。

请注意,您可以通过using two-phase commit在单个文档的ACID兼容更新之上构建交易。

答案 1 :(得分:127)

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

如果您需要从库存中删除商品并同时将其添加到某人的订单中 - 您不能。除非这两件事 - 库存和订单 - 存在于同一文件中(他们可能不会)。

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

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

这让我总是知道清单中没有项目(因为它们是由客户保留的)。当客户检查出来时,我会从reservedInventory中删除这些项目。它不是一个标准的交易,因为客户可以放弃购物车,我需要一些后台流程来查找废弃的购物车并将预留的库存移回可用的库存池。

这显然不太理想,但它是mongodb不能完美满足需求的大型应用程序的唯一部分。此外,它迄今为止完美无瑕。对于许多场景来说,这可能是不可能的,但由于我使用的文档结构,它很适合。

2)将事务数据库与MongoDB结合使用。通常使用MySQL为绝对需要它们的东西提供事务,同时让MongoDB(或任何其他NoSQL)做它最擅长的事情。

如果#1的解决方案从长远来看不起作用,我会进一步调查MongoDB与MySQL的结合,但现在#1非常适合我的需求。

答案 2 :(得分:33)

"Starbucks Does Not Use Two Phase Commit"中包含一个很好的解释。

这不是关于NoSQL数据库,但它确实说明了有时您可以承担丢失交易或暂时使数据库处于不一致状态的问题。

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

答案 3 :(得分:15)

我认为其他人已经给出了很好的答案。 但是我想补充说有ACID NOSQL数据库(如http://ravendb.net/)。所以不仅决定NOSQL - 没有ACID与ACID的关系......

答案 4 :(得分:11)

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

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

  2. 多个集合甚至同一集合中的多个文档都没有简单的“原子”更新。在大多数情况下,这不是问题,因为它可以通过Two Phase Commit来规避,或者重构您的模式,以便对单个文档进行更新。请参阅此问题:Document Databases: Redundant data, references, etc. (MongoDB specifically)

答案 5 :(得分:9)

从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

答案 6 :(得分:4)

原子修改单个集合的工作的唯一原因是因为mongodb开发人员最近使用集合范围的写锁来交换数据库锁。决定增加并发性是值得的权衡。在它的核心,mongodb是一个内存映射文件:他们已经将缓冲池管理委派给机器的虚拟机子系统。因为它总是在内存中,所以它们能够通过非常粗粒度的锁来逃脱:你将在保持它时执行仅在内存中的操作,这将非常快。这与传统的数据库系统有很大的不同,传统的数据库系统有时在持有页锁或行锁的情况下被迫执行I / O.

答案 7 :(得分:4)

请阅读ACID properties以获得更好的理解。

同样在MongoDB文档中,您可以找到question and answer

  

MongoDB不符合ACID。请阅读以下有关ACID的讨论   合规性。

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

      然而,MongoDB独立修改每个文档(对于插入和   更新);仅限文档级别,而不是多文档事务。

    1. 关于D可用性 - 您可以使用write concern选项配置此行为,但不确定。也许有人知道的更好。
    2. 我相信正在进行一些研究以使NoSQL转向ACID约束或类似。这是一个挑战,因为NoSQL数据库通常很快(呃),而ACID约束可能会显着降低性能。

答案 8 :(得分:2)

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

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

  

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

尽管可以执行 How What 操作的限制很少。

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

答案 9 :(得分:1)

如果您的存储支持每个键的线性化并进行比较和设置,那么您可以在客户端实现原子多键更新(可序列化事务)(MongoDB也是如此)。这种方法在Google's PercolatorCockroachDB中使用,但没有什么可以阻止您将其与MongoDB一起使用。

我已经创建了step-by-step visualization次此类交易。我希望它能帮助你理解它们。

如果您对读取已提交的隔离级别没有问题,那么查看Peter Bailis的RAMP transactions是有意义的。它们也可以在客户端为MongoDB实现。