一起使用MongoDB和PostgreSQL


25

我当前的项目实质上是工厂文档管理系统的运行。

就是说,有一些皱纹(惊奇,惊奇)。尽管有些皱纹是该项目特有的,但我相信会出现一些一般性的观察和问题,它们没有规范的答案(无论如何我还是可以找到),并且适用于更广泛的问题领域。这里有很多东西,我不确定它是否适合StackExchange Q&A格式,但我认为这是a)一个可以回答的问题,b)不够具体,足以使社区受益。我的某些注意事项是我特有的,但我认为该问题对于决定使用SQL,NoSQL和两者的任何人都可能有用。

背景:

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

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

复杂性:

  • 有许多不同类别的文档。这些要求已经需要数十种不同的文档。这个数字只会增加。最好的情况是我们可以利用一种简单的领域特定语言,代码生成和灵活的模式,以便领域专家无需DBA或程序员的干预即可处理新文档类的添加。(注意:已经知道我们遵守格林斯潘的第十条规则
  • 先前成功写入的完整性是该项目的核心要求。数据将对业务至关重要。如果成功写入的内容保持写入状态,则可以牺牲完整的ACID语义。
  • 这些文件本身很复杂。在我们的特定情况下,原型文档将需要每个文档实例存储150多个不同的数据。病理情况可能会恶化一个数量级,但肯定不会两个。
  • 单类文档是移动的目标,在以后的某个时间点会进行更新。
  • 当我们将其连接到关系数据库时,我们喜欢从Django获得的免费内容。我们希望保留免费赠品,而不必跳回两个Django版本来使用django-nonrel分支。完全转储ORM优于降级到1.3。

本质上,它是关系数据(用户,组等典型的Web应用程序之类的东西,以及我们需要能够实时对复杂查询进行切片和切分的文档元数据)和文档数据(例如我们不希望加入或查询的数百个字段-数据的唯一用例是显示输入该文档的单个文档)。

我想对我的首选方法进行健全性检查(如果您检查自己的发帖历史,我很清楚我不是DBA),并列举了我为其他人解决的所有选项涉及关系和非关系数据的大致相似的问题。

拟议解决方案:

1.每个文档类一张表

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

好处:

  • 标准SQL数据模型正在发挥作用。
  • 关系数据以最佳方式处理。如果需要,我们将在以后进行非规范化。
  • Django的内置管理界面非常适合内省这些表,并且ORM可以愉快地使用100%开箱即用的数据。

缺点:

  • 维护噩梦。数十个(几百个)数千列的表。
  • 应用程序级逻辑负责确定要写入哪个表。使表名成为查询的参数很糟糕。
  • 基本上,所有业务逻辑更改都将要求架构更改。
  • 病理情况可能需要在多个表中剥离单个表单的数据(请参阅:PostgreSQL表中的最大列数是多少?)。
  • 我们可能需要去寻找一个真正的,诚实的上帝DBA,毫无疑问,他最终会讨厌我们和生活。

2. EAV建模

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

好处:

  • 易于建模。

缺点:

  • 更难查询。
  • DB层不再对构成一个应用程序级对象的内容进行直接表示。
  • 我们将丢失数据库级别的约束检查。
  • 一张桌子上的行数将增长100-1000倍。从性能角度来看,可能是将来的痛点。
  • 索引可能有限。
  • 就ORM而言,DB模式是荒谬的。Web应用程序中包含的电池已保留,但自定义数据模型将需要自定义查询。

3.使用PostgreSQL的hstore或json字段

这些字段类型中的任何一个都可以解决在关系DB上下文中存储无模式数据的问题。我不立即跳到该解决方案的唯一原因是它是一个相对较新的版本(在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或内置的admin来获取文档本身的值,但这并不会造成太大的损失,因为整个应用程序基本上都是文档的管理界面,我们可能不得不自定义ORM的特定部分,使其达到无法接受的程度,以使其按我们需要的方式工作。

好处:

  • 每个后端只会做自己擅长的事情。
  • 模型之间的引用得以保留,而无需多个查询。
  • 我们必须保持Django在用户,会话等方面的作用。
  • documents无论创建多少个不同类别的文档,都只需要一个表。
  • 查询频率较低的文档数据与查询频率较高的元数据强烈分离。

缺点:

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

我会去找一个JSON数据类型的列。不要担心在Postgres中使用新功能-Postgres团队不会发布不稳定的功能。9.2实际上并不是那么新)。另外,一旦存在,您就可以使用9.3中的新JSON功能。如果您始终在处理应用程序代码中的文档(而不是使用SQL),则还可以将JSON存储在常规text列中。
a_horse_with_no_name

致潜在的回答者:请随时提供答案!由于此问题在没有“完美”答案的情况下存活了相当长的时间,因此,我打算在实施并投入生产后以完整的经验总结回答这个问题。可能是未来的一年,但请放心-OP将交付。我希望那些喜欢/赞成这个特定问题的人会发现最有用的:验证它是否有效或解释哪些障碍阻碍了并排方案。
chucksmash 2013年

2
@chucksmash。毕竟,您是否选择了#5?您如何设法实现两个数据库?您使用了哪些工具?如果没有,为什么?
xpanta

@chucksmash仍在等待您答应的反馈。
Bhashit Parikh's

@chucksmash OP未能交付... :(
阿尔伯特·罗斯曼

Answers:


13

一些想法...

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

您可以将元数据分为通用数据,类所需的数据和文档数据。在这方面,您将拥有一个包含基本公共信息的通用目录表,以及每个类一个表。在此表中,您将拥有一个hstore,json或xml字段,该字段将存储其余数据以及用于存储必须受到严格约束的数据的列。这样可以减少每个类需要在这些表中放置的内容,但是可以根据需要使用约束。这三个选项有不同的问题,值得分别考虑:

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

json是一个相当新的东西,现在并没有做很多事情。这并不意味着您不能做很多事情,但是您将不会做很多事情。如果这样做,您可能希望进行大量的编程,可能使用plv8js;或者,如果您希望使用较旧的环境,则可以使用plperlu或plpython。 json至少在当前的开发快照中,在9.3中提供了更好的支持,因此,当该版本发布时,情况会变得更好。

xml是这三种方法中得到最好支持的,功能最多,支持历史也最长。再说一遍,它是XML .....

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


1
这些都是很棒的建议。我之前曾提到过使用hstore / json(并且对xml进行了无声打折,因为xml),但是我没有想到以您推荐的方式使用它们。最重要的是,Postgres 2阶段提交建议是金。我不知道这存在。感谢您的宝贵建议。
chucksmash

2阶段提交确实是金。这使得串联使用NoSQL非常可行。尤其是如果两个DB之间的数据很少相互关联,并且它们大多解决了不同的问题
haknick

0

您可以设置查询引擎(例如PrestoDremio),以单个查询将MongoDB和Postgres中的数据连接在一起。两者都有针对每个数据库的连接器(请参阅此处此处的文档),并建议分别运行“ SQL on any”和“ join any”。

要测试Presto,您可以在带有Hadoop,Hive和Presto的AWS EMR上部署一个小型集群(如果不想使用命令行,请添加色相),该框可在框中使用-确保按照以下说明进行设置连接器。Hive不是严格必需的,但是有了它,您可以使用Mongo和Postgres之间的联接结果创建表(请查看此页面以获取示例)。市场上还有一个付费版本,该付费版本经过了(据说)最优化,并且有30天的试用期。

我没有使用过Dremio,但是也有一些简单的方法可以将其部署到AWS,Azure或本地。他们在他们的网站上提供了一些在线课程,并可以使用“虚拟实验室”,您可以免费使用这些课程来跟随课程。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.