MongoDB Schema Design - 许多小文档还是更少的大文档?

Posted

技术标签:

【中文标题】MongoDB Schema Design - 许多小文档还是更少的大文档?【英文标题】:MongoDB Schema Design - Many small documents or fewer large documents? 【发布时间】:2011-03-03 14:16:33 【问题描述】:

背景 我正在对从我们的 RDBMS 数据库到 MongoDB 的转换进行原型设计。在进行非规范化时,似乎我有两种选择,一种会导致许多(数百万)个小文档,另一种会导致更少(数十万)个大文档。

如果我可以将其提炼成一个简单的类比,这将是具有较少客户文档的集合之间的区别(在 Java 中):

类客户 私有字符串名称; 私人地址地址; // 每个 CreditCard 都有数百个 Payment 实例 私人 Set 信用卡;

或者一个包含很多很多这样的付款文档的集合:

类付款 私人客户客户; 私人信用卡信用卡; 私人日期 payDate; 私人浮动支付金额;

问题 MongoDB 的设计是偏爱很多很多小文档还是更少的大文档?答案是否主要取决于我计划运行的查询? (即客户 X 有多少张信用卡?vs 所有客户上个月支付的平均金额是多少?)

我环顾四周,但没有偶然发现任何可以帮助我回答问题的 MongoDB 架构最佳实践。

【问题讨论】:

【参考方案1】:

您肯定需要针对正在执行的查询进行优化。

根据您的描述,这是我的最佳猜测。

您可能想知道每个客户的所有信用卡,因此请在客户对象中保留一个数组。您可能还希望每次付款都有一个客户参考。这将使支付文档相对较小。

支付对象将自动拥有自己的 ID 和索引。您可能还想在客户参考上添加一个索引。

这将允许您快速搜索客户付款,而无需每次都存储整个客户对象。

如果您想回答诸如“所有客户上个月支付的平均金额是多少”之类的问题,您将需要一个地图/减少任何大型数据集。您没有“实时”获得此响应。您会发现存储对 Customer 的“引用”对于这些 map-reduce 来说可能已经足够了。

所以直接回答你的问题:MongoDB 是设计成更喜欢许多小文档还是更少的大文档?

MongoDB 旨在非常快速地找到索引条目。 MongoDB 非常擅长大海捞针。 MongoDB 不是非常善于在大海捞针中找到大部分。因此,围绕您最常见的用例构建数据,并为罕见的用例编写 map/reduce 作业。

【讨论】:

【参考方案2】:

根据 MongoDB 自己的文档,听起来它是为许多小文档而设计的。

来自Performance Best Practices for MongoDB:

MongoDB 中文档的最大大小为 16 MB。在实践中最 文档只有几千字节或更少。考虑文档更像 表中的行比表本身。而不是维持 单个文档中的记录列表,而不是使每个记录 文件。

来自6 Rules of Thumb for MongoDB Schema Design: Part 1:

一对一建模

“一对多”的一个例子可能是一个人的地址。这 是一个很好的嵌入用例——你可以把地址放在一个数组中 在您的 Person 对象内部。

一对多

“一对多”的一个例子可能是一个产品的零件 备件订购系统。每个产品最多可以有几个 数百个替换零件,但绝不超过几千个或 所以。这是一个很好的引用用例——你可以把 ObjectID 产品文档中数组中的部件。

一对Squillions

“one-to-squillions”的一个例子可能是事件记录系统 收集不同机器的日志消息。任何给定的主机 可以生成足够的消息来溢出 16 MB 的文档大小, 即使您存储在数组中的所有内容都是 ObjectID。这是 “父引用”的经典用例——你有一个文档 主机,然后将主机的 ObjectID 存储在文档中 日志消息。

【讨论】:

【参考方案3】:

随着时间的推移大幅增长的文档可能是定时炸弹。网络带宽和 RAM 使用可能会成为可衡量的瓶颈,迫使您重新开始。

首先,让我们考虑两个集合:Customer 和 Payment。因此,粒度相当小:每次付款一份文件。

接下来,您必须决定如何对帐户信息(例如信用卡)进行建模。让我们考虑一下客户文档是否包含帐户信息数组,或者您是否需要一个新的帐户集合。

如果帐户文档与客户文档分开,则将一个客户的所有帐户加载到内存中需要获取多个文档。这可能会转化为额外的内存、I/O、带宽和 CPU 使用率。这是否立即意味着收集帐户是一个坏主意?

您的决定会影响付款文件。如果帐户信息嵌入在客户文档中,您将如何引用它?单独的帐户文档有自己的 _id 属性。使用嵌入的帐户信息,您的应用程序将为帐户​​生成新的 ID,或使用帐户的属性(例如,帐号)作为密钥。

付款文件是否可以实际包含在固定时间范围内(例如,一天)进行的所有付款。这种复杂性将影响读取和写入支付文档的所有代码。过早的优化对项目来说可能是致命的。

与帐户文档一样,只要付款文档仅包含一笔付款,就可以轻松引用付款。一种新型的单据,例如信用证,可以参考付款。但是,您会创建信用收款,还是将信用信息嵌入付款信息中?如果您以后需要参考信用会发生什么?

总而言之,我已经成功地处理了许多小文档和许多收藏。我使用 _id 并且仅使用 _id 实现引用。因此,我不必担心不断增长的文档会破坏我的应用程序。该模式易于理解和索引,因为每个实体都有自己的集合。重要实体并未隐藏在其他文档中。

我很想听听您的发现。祝你好运!

【讨论】:

以上是关于MongoDB Schema Design - 许多小文档还是更少的大文档?的主要内容,如果未能解决你的问题,请参考以下文章

MongoDB Schema Design - 许多小文档还是更少的大文档?

MongoDB 一对多关系建模

MongoDB对象之间的关系

MongoDB聚合时间序列

MongoDB、时间序列和聚合框架

如何在 MongoDB 中聚合时间序列数据