【问题标题】:How to handle application death and other mid-operation faults with Mongo DB如何使用 Mongodb 处理应用程序死亡和其他运行中故障
【发布时间】:2015-04-03 02:29:45
【问题描述】:

由于 Mongo 没有可用于确保不会向数据库提交任何内容的事务,除非其一致(非损坏)数据,如果我的应用程序在写入一个文档和进行相关写入之间死亡另一个文档,我可以使用哪些技术来删除损坏的数据和/或以某种方式恢复?

【问题讨论】:

  • 原子数据应始终准确写入一个文档。虽然这并不总是可能的(这很可能表明 MongoDB 是不适合使用的 DBMS),但适当的数据建模通常会提供解决方案。请描述您的用例并向我们展示您的数据模型——通常可以找到解决方案。
  • 感谢您提供的帮助,但我正在寻找更通用的技术。如果你能用几个不同的例子来写一个答案,那就太好了,每个例子都展示了不同的“正确的数据建模”技术(也许一个展示了 Mongo 肯定是错误的 DBMS)。
  • 那将超出范围。为 MongoDB 写一本关于数据建模的书相对容易;)一般来说:将原子数据写入一个文档。一个例子是为一个网上商店编写一个订单文件,其中包括给定时间点的价格。如果应用程序死了,不会造成任何伤害,除了立即订单文件的丢失,这应该很容易重新创建篮子。没有用例和对象的属性,几乎有无限可能。
  • 是的,如果您尝试用“我有一个带有购物车的网上商店等等等等...但我不认为要求列出一般类型问题及其解决方案的范围。
  • 例如,适合我部分情况的示例是我有一个权限文档,该文档定义了有权访问我的主数据文档的用户列表。在这种情况下,灾难性故障可能会留下未使用的权限文档,或者在数据文档中留下错误的 _id。这里的解决方案是先创建 Permission,然后将其 _id 写入 Data 文档,因为拥有未使用的 Permission 比在 Data 文档中拥有错误的 _id 更好。这就是我所说的泛化。有哪些场景?它不需要详尽

标签: mongodb corruption


【解决方案1】:

NoSQL 背后的更大理念是针对特定问题使用精心建模的数据结构,而不是用锤子敲击每个问题。对于应该称为“短期事务”的事务也是如此,因为典型的 RDBMS 事务对“真实”的长期事务几乎没有帮助。

RDBMS 支持的事务类型通常只是因为有限的数据模型迫使您将数据存储在多个表中,而不是使用嵌入式数组(想想典型的发票/发票项目示例)。

在 MongoDB 中,尝试使用大量写入、去规范化的数据结构,并将数据保存在单个文档中,这样可以提高读取速度、数据局部性并确保一致性。这样的数据模型也更容易扩展,因为单次读取只会命中单台服务器,而不必从多个来源收集数据。

但是,在某些情况下,必须在各种上下文中读取数据并且去规范化变得不可行。在这种情况下,您可能想看看Two-Phase Commits 或选择完全不同的并发方法,例如MVCC(一句话,svn、git 等就是这样做的)。然而,后者几乎不是 RDBM 的直接替代品,而是向更高级别的应用程序(如果不是用户)公开了一种完全不同的并发性。

【讨论】:

  • 所以你真的是说,一旦我不能把所有东西都写到一个文档中,我就应该看看 MVCC 和两阶段提交吗?肯定有一些介于两者之间的东西。
  • 是的。当然,您可以简单地接受您在上一条评论中描述的未使用的文档,但这种方法很难“以某种方式删除或恢复损坏的数据”。编写一个更干净的应用程序来删除缺少引用的此类文档是微不足道的,但通常不值得付出努力,因为您可能每年不需要它超过一次......如果您一直需要它,它意味着您的应用程序不断崩溃,这本身就是一个大问题......
  • 如果灾难性故障意味着您的应用程序在您的公司可以争先恐后地纠正其数据之前完全崩溃,那么即使是一天的停机时间也可能对企业造成难以置信的打击。我会说,如果发生这种故障,弄清楚你的数据会发生什么是值得的。
【解决方案2】:

我自己考虑一下,我想确定一些影响类别:

  1. 您的操作只有一个数据库保存(将数据保存到一个文档中)
  2. 您的操作有两个数据库保存(更新、插入或删除),A 和 B
    1. 他们是独立的
    2. A 需要 B 才能有效
    3. 它们是相互依赖的(B 有效需要 A,A 有效需要 B)
  3. 您的操作有两个以上的数据库保存

我认为这是一般可能性的完整列表。在情况 1 中,您没有问题 - 一个数据库保存是原子的。在案例 2.1 中,同样的事情,如果它们是独立的,它们也可能是两个独立的操作。

对于案例 2.2,如果你先做 A 然后做 B,最坏的情况是你会有一些额外的数据(B 数据)会占用你的系统空间,但否则是无害的。在案例 2.3 中,如果发生灾难性故障,您可能会有一些损坏的数据。而案例 3 只是案例 2 的组合。

不同情况的一些例子:

1.0。您将汽车文件的颜色更改为“蓝色”

2.1。您将汽车文件的颜色更改为“红色”并且将驾驶员的头发颜色更改为“红色”

2.2。您创建一个新的引擎文档并将其 ID 添加到汽车文档中

2.3.a.您将汽车的“gasType”更改为“diesel”,这需要将您的发动机更改为“diesel”类型的发动机。

2.3.b。 2.3 的另一个例子:你将汽车文档 A 连接到另一个汽车文档 B,A 将“towedBy”属性设置为 B 的 ID,B 将“拖曳”属性设置为 A 的 ID

3.0。我会把这方面的例子留给你想象

在许多情况下,可以将 2.3 场景转换为 2.2 场景。在 2.3.a 示例中,汽车文档和引擎是单独的文档。对于这个例子,让我们忽略将引擎放在汽车文档中的可能性。拥有柴油机和非柴油机 拥有非柴油机和柴油机均无效。所以他们都必须改变。但是根本没有发动机并且有柴油可能是有效的。因此,您可以添加一个步骤,使整个事情在所有点上都有效。首先,拆下发动机,然后更换汽油,然后更改发动机类型,最后将发动机装回汽车上。

如果您会从 2.3 方案中获取损坏的数据,您将需要一种检测损坏的方法。在示例 2.3.b 中,如果一个文档具有“towing”属性,但另一个文档没有相应的“towedBy”属性,事情可能会中断。因此,这可能是在发生灾难性故障后需要检查的东西。查找所有具有“拖曳”的文档,但该属性中具有 id 的文档没有将其“towedBy”设置为正确的 ID。可以选择删除“towing”属性或设置适当的“towedBy”属性。它们看起来同样有效,但这可能取决于您的应用程序。

在某些情况下,您可能能够找到这样的损坏数据,但在设置这些内容之前您不会知道数据是什么。在这些情况下,设置默认值可能总比没有好。某些类型的损坏比其他类型的损坏要好(尤其是那些会导致应用程序出错而不是简单地显示数据不正确的损坏)。

如果上述类型的代码分析或损坏修复变得不可行,或者如果您想完全避免任何数据损坏,您最后的手段是采纳 mnemosyn 的建议并实施Two-Phase CommitsMVCC,或类似的东西,可让您识别和回滚不确定状态的更改。

【讨论】:

  • 只处理插入。将更新考虑在内,它变得更加复杂,因为数据可能无效,但看起来有效
  • 我的类别包括更新。 “保存”是任何更新、插入或删除。这些类别未涵盖哪些示例?
  • 您的操作不是原子的。可能还有第二个作家。线程 A 更新项目 1。现在线程 B 查看项目 1,将其识别为处于可以对相关项目 2 执行自己的更新的状态。线程 B 立即覆盖该更改(或执行一致性检查并失败,因为线程B 更快)。这会导致拜占庭错误。您正在减少网络分区/机器崩溃的所有故障,但错误的代码和多线程是更常见的问题来源。
  • 好的。我的问题与“错误的代码和多线程”无关,所以这些都是题外话。我的问题是特别是关于机器崩溃和其他运行中的应用程序故障引起的问题。
猜你喜欢
  • 1970-01-01
  • 2010-12-09
  • 1970-01-01
  • 2011-01-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多