我不是数据库专家,也没有正式的计算机科学背景,所以请原谅我。我想知道如果您使用v4之前的旧版本MongoDB(不兼容ACID),会发生哪些现实世界中的负面事情。这适用于任何不符合ACID要求的数据库。

我知道MongoDB可以执行原子操作,但它们不“支持传统的锁定和复杂的事务”,主要是出于性能原因。我也理解数据库事务的重要性,举个例子,当你的数据库是一家银行的,你正在更新几个记录,这些记录都需要同步,如果停电,你希望事务恢复到初始状态,所以信用等于购买,等等。

但是当我开始谈论MongoDB时,我们中那些不知道数据库如何实际实现的技术细节的人开始抛出这样的语句:

MongoDB比MySQL和Postgres快得多,但它“不能正确保存”的几率很小,比如百万分之一。

That "won't save correctly" part is referring to this understanding: If there's a power outage right at the instant you're writing to MongoDB, there's a chance for a particular record (say you're tracking pageviews in documents with 10 attributes each), that one of the documents only saved 5 of the attributes… which means over time your pageview counters are going to be "slightly" off. You'll never know by how much, you know they'll be 99.999% correct, but not 100%. This is because, unless you specifically made this a mongodb atomic operation, the operation is not guaranteed to have been atomic.

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

我只是在寻找可能的一个列表,当/为什么负面的事情发生在一个ACID不合规的数据库,如MongoDB,理想情况下,如果有一个标准的解决方案(如运行后台作业来清理数据,或仅使用SQL等)。


当前回答

从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

其他回答

MongoDB不兼容acid的说法实际上是不对的。相反,MongoDB是文档级的acid编译器。

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

原子性:要么完全完成,要么没有 一致:没有读者会看到“部分应用”的更新 孤立的:同样,没有读者会看到“肮脏”的阅读 持久的:(带有适当的写作关注点)

MongoDB没有事务——也就是说,可以回滚且与acid兼容的多文档更新。

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

请阅读ACID属性以更好地理解。

此外,在MongoDB文档中,您可以找到一个问题和答案。

MongoDB不兼容ACID。阅读下面关于ACID的讨论 遵从性。

MongoDB is Atomic on document level only. It does not comply with the definition of atomic that we know from relational database systems, in particular the link above. In this sense MongoDB does not comply with the A from ACID. MongoDB is Consitent by default. However, you can read from secondary servers in a replica set. You can only have eventual consistency in this case. This is useful if you don't mind to read slightly outdated data. MongoDB does not guarantee Isolation (again according to above definition):

对于同时有多个读取器和写入器的系统,MongoDB可以 操作之前,允许客户端读取写入操作的结果 写操作返回。 如果mongod在日志提交之前终止,即使是写操作 返回成功,则查询可能已读取不存在的数据 mongod重启后。 但是,MongoDB会单独修改每个文档(用于插入和 更新);仅在文档级别上,而不在多文档事务上。

关于持久性——你可以用写关注选项来配置这种行为,但不确定。也许有人更清楚。

我相信一些正在进行的研究将NoSQL转向ACID约束或类似的约束。这是一个挑战,因为NoSQL数据库通常比较快,而ACID约束会显著降低性能。

在“星巴克不使用两阶段提交”中有一个很好的解释。

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

我不认为这是需要“修复”的东西。解决办法是使用acid兼容的关系数据库。当NoSQL的行为符合应用程序需求时,您可以选择一个NoSQL替代方案。

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

如果你需要从库存中移除一件物品,同时将其添加到某人的订单中——你不能这样做。除非这两样东西——库存和订单——存在于同一个文档中(它们很可能不存在)。

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

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

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

这显然不太理想,但这是大型应用程序中mongodb不能完美满足需求的唯一部分。此外,到目前为止,它工作得完美无缺。对于许多场景来说,这可能不太可能,但是由于我使用的文档结构,它非常适合。

2)与MongoDB一起使用事务性数据库。通常使用MySQL为绝对需要它们的事情提供事务,而让MongoDB(或任何其他NoSQL)做它最擅长的事情。

如果从长远来看,我的第一个解决方案不起作用,我将进一步研究结合MongoDB和MySQL,但目前第一个解决方案很适合我的需求。

The only reason atomic modifies work against a single-collection is because the mongodb developers recently exchanged a database lock with a collection wide write-lock. Deciding that the increased concurrency here was worth the trade-off. At it's core, mongodb is a memory-mapped file: they've delegated the buffer-pool management to the machine's vm subsystem. Because it's always in memory, they're able to get away with very course grained locks: you'll be performing in-memory only operations while holding it, which will be extremely fast. This differs significantly from a traditional database system which is sometimes forced to perform I/O while holding a pagelock or a rowlock.