我当前的项目实质上是工厂文档管理系统的运行。
就是说,有一些皱纹(惊奇,惊奇)。尽管有些皱纹是该项目特有的,但我相信会出现一些一般性的观察和问题,它们没有规范的答案(无论如何我还是可以找到),并且适用于更广泛的问题领域。这里有很多东西,我不确定它是否适合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
列中。