MongoDB在v4之前不兼容ACID意味着什么?

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

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

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

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

“不能正确保存”的部分指的是这样的理解:如果在你写入MongoDB的那一刻停电了,有一个特定的记录(比如你在跟踪每个有10个属性的文档中的页面视图),其中一个文档只保存了5个属性……这意味着随着时间的推移,你的页面视图计数器将“略微”关闭。你永远不知道误差有多大,你知道他们会有99.999%的正确率,但不是100%。这是因为,除非你特别将其设置为Mongodb原子操作,否则不能保证该操作是原子操作。

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

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

62882 次浏览

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

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

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

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

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

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

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

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

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

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

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

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

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

  2. 对多个集合甚至同一集合中的多个文档进行“原子”更新是不容易的。在大多数情况下,这不是问题,因为它可以通过Two Phase Commit来规避,或者重新构造您的模式,以便对单个文档进行更新。请看这个问题:文档数据库:冗余数据,引用,等(MongoDB具体) .

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

原子修改对单个集合有效的唯一原因是mongodb开发人员最近用一个集合范围的写锁交换了一个数据库锁。决定这里增加的并发性是值得的。mongodb的核心是一个内存映射文件:他们将缓冲池管理委托给了机器的vm子系统。因为它总是在内存中,所以它们能够使用非常严格的锁:当持有它时,您将只在内存中执行操作,这将非常快。这与传统的数据库系统有很大的不同,传统的数据库系统有时被迫在持有页面锁或行锁的同时执行I/O。

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

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

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

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

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

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

同样在MongoDB文档中,你可以找到问答

MongoDB不兼容ACID。阅读下面关于ACID的讨论 合规。< / p >
  1. MongoDB仅在文档级别上是Atomic。它不符合我们从关系数据库系统中了解到的原子的定义,特别是上面的链接。从这个意义上说,MongoDB不符合ACID的A。
  2. MongoDB默认为__abcononsitent。 但是,可以从副本集中的次要服务器读取。在这种情况下你只能具有最终的一致性。如果您不介意阅读稍微过时的数据,这是有用的。
  3. MongoDB不保证Isolation(还是根据上面的定义):
    对于同时有多个读取器和写入器的系统,MongoDB将 操作之前,允许客户端读取写入操作的结果 写操作返回
  1. 如果mongod在日志提交之前终止,即使是写操作 返回成功,则查询可能已读取不存在的数据
  2. . .

然而, MongoDB在隔离中修改每个文档(用于插入和 更新);

.

.
  1. 关于Durability -你可以用write concern选项配置此行为,但不确定。也许有人更清楚。

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

如果您的存储支持每个键的线性化和比较和设置(对于MongoDB是这样的),您可以在客户端实现原子多键更新(可序列化的事务)。这种方法在谷歌的过滤器CockroachDB中使用,但没有什么可以阻止你在MongoDB中使用它。

我已经创建了这样的事务的一步一步的可视化。我希望它能帮助你理解它们。

如果你对读提交隔离级别满意,那么看一看Peter Bailis的坡道的事务是有意义的。它们也可以在客户端为MongoDB实现。

从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中,单个文档上的操作是原子的”- 这是过去的事情

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

但是,对于需要对多个文档进行原子性更新或需要对多个文档进行一致性读取的情况,MongoDB提供了针对副本集执行多文档事务的能力。多文档事务可以跨多个操作、集合、数据库和文档使用。多文档事务提供了一个“全有或全无”的命题。当事务提交时,将保存事务中所做的所有数据更改。如果事务中的任何操作失败,则事务将终止,事务中所做的所有数据更改将被丢弃,而不会变得可见。在事务提交之前,事务中的写操作在事务外部是不可见的。

虽然可以执行如何什么操作有一些限制。

检查Mongo Doc。 https://docs.mongodb.com/master/core/transactions/ < / p >