背景
我正在为从RDBMS数据库到MongoDB的转换进行原型设计。在非规范化的过程中,似乎我有两个选择,一个导致许多(数百万)较小的文档,或者一个导致较少(数十万)大文档。
如果我可以将它提炼成一个简单的类比,那么像这样的(在Java中)客户文档较少的集合之间的区别就在于:
class Customer { private String name; private Address address; // each CreditCard has hundreds of Payment instances private Set<CreditCard> creditCards; }
或包含许多此类付款文件的集合:
class Payment { private Customer customer; private CreditCard creditCard; private Date payDate; private float payAmount; }
问题
MongoDB是否设计为偏好许多,许多小文档或更少的大型文档?答案主要取决于我计划运行的查询吗? (即客户X有多少张信用卡?vs上个月所有客户支付的平均金额是多少?)
我看了很多,但我没有偶然发现任何可以帮助我回答问题的MongoDB架构最佳实践。
答案 0 :(得分:77)
您肯定需要针对您正在进行的查询进行优化。
根据您的描述,这是我最好的猜测。
您可能想知道每个客户的所有信用卡,因此请在客户对象中保留一组信用卡。您可能还希望为每个付款设置一个客户参考。这将使付款文件相对较小。
Payment对象将自动拥有自己的ID和索引。您可能也想在Customer引用上添加索引。
这样您就可以快速搜索客户付款,而无需每次都存储整个客户对象。
如果您想回答“上个月所有客户支付的平均金额是多少”这样的问题,那么您需要为任何规模庞大的数据集寻找map / reduce。你没有“实时”得到这个回应。你会发现存储一个“引用”给客户可能足以让这些地图减少。
所以直接回答你的问题: MongoDB是设计用于偏好许多很小的文档还是更少的大文档?
MongoDB旨在快速查找索引条目。 MongoDB非常擅长在大型干草堆中找到少数针。 MongoDB 非常擅长在大海捞针中找到大多数的针头。因此,围绕最常见的用例构建数据,并为罕见的用例编写map / reduce作业。
答案 1 :(得分:17)
根据MongoDB自己的文档,它听起来像是为许多小文档设计的。
来自Performance Best Practices for MongoDB:
MongoDB中文档的最大大小为16 MB。在实践中最多 文件是几千字节或更少。考虑更像的文件 表中的行而不是表本身。而不是维持 单个文档中的记录列表,而是使每个记录成为一个 文档。
来自6 Rules of Thumb for MongoDB Schema Design: Part 1:
一对一建模
“一对一”的例子可能是一个人的地址。这个 嵌入是一个很好的用例 - 你将地址放在一个数组中 在你的Person对象里面。
<强> 1对许多强>
“一对多”的一个例子可能是一个产品的一部分 更换零件订购系统。每种产品可能有多达几种 一百个更换零件,但从不超过几千或 所以。这是一个很好的引用用例 - 你把ObjectIDs放在了 产品文档中数组中的部分。
<强> 1对Squillions 强>
“one-to-squillions”的一个例子可能是事件记录系统 收集不同机器的日志消息。任何给定的主机 可以生成足够的消息来溢出16 MB的文档大小, 即使您存储在数组中的所有内容都是ObjectID。这是 “父参考”的经典用例 - 你有一个文件 主机,然后将主机的ObjectID存储在文档中 日志消息。
答案 2 :(得分:8)
随着时间的推移而大幅增长的文件可能是时间炸弹。网络带宽和RAM使用率可能会成为可衡量的瓶颈,迫使您重新开始。
首先,让我们考虑两个集合:客户和付款。因此,粮食相当小:每笔付款一份文件。
接下来,您必须决定如何为帐户信息建模,例如信用卡。让我们考虑一下客户文档是否包含帐户信息数组,或者您是否需要新的帐户集合。
如果帐户文档与客户文档分开,则将一个客户的所有帐户加载到内存中需要获取多个文档。这可能会转化为额外的内存,I / O,带宽和CPU使用率。这是否意味着账户收集是一个坏主意?
您的决定会影响付款文件。如果帐户信息嵌入客户文档中,您将如何引用它?单独的帐户文档具有自己的_id属性。借助嵌入式帐户信息,您的应用会为帐户生成新的ID,或者使用帐户的密钥属性(例如帐号)。
付款单据是否实际包含在固定时间范围内(例如,日期?)进行的所有付款。这种复杂性将影响所有读取和写入付款文档的代码。过早优化对项目来说可能是致命的。
与帐户文档一样,只要付款单据只包含一笔付款,就可以轻松引用付款。例如,一种新类型的文档可以引用付款。但是,您是否会创建一个信用卡集合,或者您是否将信用信息嵌入到付款信息中?如果您以后需要参考信用证会怎么样?
总而言之,我成功地获得了许多小文档和许多集合。我用_id实现引用,只用_id实现。因此,我不担心不断增长的文档会破坏我的应用程序。模式易于理解和索引,因为每个实体都有自己的集合。重要实体不会隐藏在其他文档中。
我很想知道你的发现。祝你好运!