结合使用 MongoDB 和 PostgreSQL

chu*_*ash 28 rdbms postgresql document-oriented mongodb

我目前的项目本质上是一个工厂文件管理系统的运行。

也就是说,有一些皱纹(惊喜,惊喜)。虽然有些问题是项目特有的,但我相信有一些普遍的观察结果和问题没有规范的答案(无论如何我可以找到)并且适用于更广泛的问题领域. 这里有很多,我不确定它是否适合 StackExchange Q&A 格式,但我认为它 a) 一个可回答的问题和 b) 不够具体,它可以使社区受益。我的一些考虑是特定于我的,但我认为这个问题对任何面临决定 SQL 还是 NoSQL 还是两者的人都有用。

背景:

我们正在构建的 Web 应用程序包含本质上具有明显关系的数据以及面向文档的数据。我们想吃蛋糕,也想吃。

TL;DR:我认为下面的 #5 通过了气味测试。你?有没有人有将 SQL 和 NOSQL 集成到单个应用程序中的经验?我试图在下面列出解决此类问题的所有可能方法。我错过了一个有前途的替代方案吗?

复杂性:

  • 有许多不同类别的文档。这些要求已经需要数十种不同的文件。这个数字只会上升。最好的情况是,我们可以利用简单的领域特定语言、代码生成和灵活的模式,以便领域专家可以在没有 DBA 或程序员干预的情况下处理新文档类的添加。(注意:已经意识到我们正在实践Greenspun 的第十条规则
  • 以前成功写入的完整性是项目的核心要求。这些数据对业务至关重要。可以牺牲写入的完整 ACID 语义,前提是成功写入的内容保持写入状态。
  • 文件本身很复杂。在我们的特定案例中,原型文档需要为每个文档实例存储 150 多个不同的数据片段。病理情况可能会更糟一个数量级,但肯定不是两个。
  • 一类文档是一个移动的目标,在稍后的时间点会更新。
  • 当我们将 Django 连接到关系数据库时,我们喜欢从 Django 获得的免费东西。我们希望保留免费赠品,而不必跳回两个 Django 版本来使用 django-nonrel fork。完全转储 ORM 比降级到 1.3 更可取。

从本质上讲,它是关系数据(您的典型 Web 应用程序内容,如用户、组等,以及我们需要能够实时对复杂查询进行切片和切块的文档元数据)和文档数据(例如我们没有兴趣加入或查询的数百个字段 - 我们对数据的唯一用例将是显示输入的单个文档)。

我想对我的首选方法进行完整性检查(如果你检查我的发帖历史,我非常明确地说明我不是 DBA),并列举我遇到的所有选项供其他人解决涉及关系和非关系数据的广泛相似的问题。

建议的解决方案:

1. 每个文档类一张表

每个文档类都有自己的表,其中包含所有元数据和数据的列。

好处:

  • 标准 SQL 数据模型正在发挥作用。
  • 以最好的方式处理关系数据。如果需要,我们稍后会进行非规范化。
  • Django 的内置管理界面可以轻松地检查这些表,并且 ORM 可以 100% 使用开箱即用的数据。

缺点:

  • 维修噩梦。数十(数百?)个表(数十?)数千列。
  • 应用程序级逻辑负责准确决定要写入哪个表。将表名作为查询的参数很糟糕。
  • 基本上所有业务逻辑更改都需要架构更改。
  • 病理情况可能需要为跨多个表的单个表单条带化数据(请参阅:PostgreSQL 表中的最大列数是多少?)。
  • 我们可能需要去找一个真正的、对上帝诚实的 DBA,他无疑最终会憎恨生活和我们。

2.EAV建模

只有一个字段表。实体-属性-值建模已经很好理解了。为了完整起见,我已将其包括在内。我认为 2013 年启动的任何新项目都不会有意采用 EAV 方法。

好处:

  • 易于建模。

缺点:

  • 更难查询。
  • DB 层不再直接表示构成一个应用程序级对象的内容。
  • 我们将失去 DB 级别的约束检查。
  • 一个表上的行数将增长 100 到 1000 倍。未来可能的痛点,性能方面。
  • 有限的索引可能。
  • 就 ORM 而言,DB 模式是荒谬的。包含网络应用程序的电池会被保留,但自定义数据模型将需要自定义查询。

3. 使用 PostgreSQL hstore 或 json 字段

这些字段类型中的任何一种都可以在关系数据库的上下文中存储无模式数据。我没有立即跳到这个解决方案的唯一原因是它相对较新(在 8.4 版中引入,所以不是那么新),我之前对它的接触为零,我很怀疑。这让我觉得是错误的,原因与我将所有漂亮的、易于规范化的数据扔进 Mongo 时感到不安的原因完全相同——即使 Mongo 可以处理文档之间的引用。

好处:

  • 我们获得了 Django ORM 和内置身份验证和会话管理的好处。
  • 一切都保留在我们之前成功用于其他项目的一个后端中。

缺点:

  • 个人没有这方面的经验。
  • 它看起来不像是一个非常常用的功能。看起来他们向查看 NOSQL 解决方案的人推荐了很多,但我没有看到很多证据表明他们被选中。这让我觉得我一定错过了一些东西。
  • 存储的所有值都是字符串。丢失数据库级约束检查。
  • hstore 中的数据永远不会显示给用户,除非他们专门查看文档,但存储在更标准列中的元数据将会显示。我们将击败该元数据,我担心我们将创建的相当大的 hstore 可能会带来性能缺陷。

4. 完全面向文档

把所有的东西都记录下来(在 MongoDB 的意义上)。创建一个类型的集合Document并称之为一天。将所有外围数据(包括有关用户帐户、组等的数据)也带入 mongo。这个解决方案显然比 EAV 建模更好,但我感觉不对,原因与 #3 感觉不对 - 他们都觉得把你的锤子也当作螺丝刀。

好处:

  • 无需预先对数据建模。有一个包含类型文档的集合,Document然后就收工了。
  • 已知良好的缩放特性,如果集合需要增长到包含数百万甚至数十亿的文档。
  • JSON 格式 (BSON) 对开发人员来说很直观。
  • 据我所知(此时只是模糊),通过对写入关注级别的偏执,即使是单个实例也可以在任何事情发生时提供非常强大的数据安全性,直到硬盘崩溃。

缺点:

  • ORM 是 Django 主干的窗口。随之而来的免费赠品:身份验证框架、会话框架、管理界面,当然还有许多其他东西。
  • 必须使用 mongo 的引用功能(需要多次查询)或非规范化数据。我们不仅失去了从 Django 获得的免费赠品,还失去了我们在 PostgreSQL 中认为理所当然的 JOIN 等赠品。
  • 数据安全。当人们读到 MongoDB 时,似乎总有至少一个人提到它将如何启动和丢失您的数据。他们从不引用特定的事件,这可能只是胡说八道,或者只是与旧的默认火灾有关而忘记了写关注,但它仍然让我担心。我们当然会在任何情况下使用一种相当偏执的备份策略(如果数据被悄悄损坏,当然这很可能无关紧要......)。

5. PostgreSQL 和 MongoDB

关系数据进入关系数据库,文档数据进入面向文档的数据库。在documents关系数据库表包含了所有我们可能需要索引或切片和切块上,当我们需要查询的文件中的字段的实际值,我们将利用这些数据,以及一个MongoDB中的ObjectId的。我们将无法使用 ORM 或内置管理员来处理文档本身的值,但这并不是什么大损失,因为整个应用程序基本上是文档的管理界面,我们可能不得不这样做将 ORM 的特定部分定制到不可接受的程度,使其按照我们需要的方式工作。

好处:

  • 每个后端只做它擅长的事情。
  • 无需多次查询即可保留模型之间的引用。
  • 就用户、会话等而言,我们可以保留 Django 给我们的电池。
  • documents无论创建多少不同类别的文档,都只需要一张表。
  • 不太经常查询的文档数据与经常查询的元数据有很大的区别。

缺点:

  • 检索文档数据将需要 2 个顺序查询,首先针对 SQL DB,然后针对 MongoDB(尽管这并不比将相同数据存储在 Mongo 中且未非规范化更糟)
  • 写作将不再是原子性的。对单个 Mongo 文档的写入保证是原子性的,PG 显然可以保证原子性,但确保两者之间的写入原子性将需要应用程序逻辑,毫无疑问会降低性能和复杂性。
  • 两个后端 = 两种查询语言 = 具有不同管理要求的两个不同程序 = 两个争夺内存的数据库。

Chr*_*ers 14

一些想法……

通常人们不想在不同的系统中存储紧密相关的信息片段。事情不同步的可能性很大,现在你手上的问题不是一个,而是两个。你可以用 Mongo 做的一件事是使用它来输入或输出数据。我的偏好是尽可能将所有内容保留在 PostgreSQL 中。但是,我要指出,这样做确实需要 PostgreSQL 编程的专业知识,而不适合不愿意致力于使用高级功能的商店。我看到的选项集与您略有不同。由于我的偏好不是我看到的列表,因此我将其提供给您。

您可能可以将元数据分成公共数据、类所需的数据和文档数据。在这方面,您将拥有一个包含基本公共信息的通用目录表,以及每个类一个表。在此表中,您将有一个 hstore、json 或 xml 字段,这些字段将存储其余数据以及存储必须受到显着约束的数据的列。这将减少您需要在每个类的这些表中放入的内容,但允许您随意利用约束。这三个选项有不同的问题,值得分别考虑:

hstore相对有限,但也被很多人使用。它不是非常新,但它只是一个键/值存储,并且不能嵌套数据结构,与 json 和 xml 不同。

json是很新的,现在并没有做很多事情。这并不意味着你不能用它做很多事情,但你不会开箱即用。如果你这样做,你可以期望做大量的编程,可能在 plv8js 中,或者,如果你想坚持使用旧环境,plperlu 或 plpython。 json在 9.3 中得到了更好的支持,但至少在当前的开发快照中是这样,所以当该版本发布时,事情会变得更好。

xml是三者中支持最好的,功能最多,支持历史最长。再说一遍,它是XML.....

但是,如果您决定将 Mongo 和 PostgreSQL 一起使用,请注意 PostgreSQL 支持 2 阶段提交,这意味着您可以运行写入操作,然后发出PREPARE TRANSACTION,如果成功,则在 Mongo 中进行原子写入。如果成功,则可以COMMIT在 PostgreSQL 中。