MongoDB架构设计 - 许多小文档或更少的大型文档?

时间:2010-06-14 15:56:13

标签: database-design schema mongodb

背景
我正在为从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架构最佳实践。

3 个答案:

答案 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实现。因此,我不担心不断增长的文档会破坏我的应用程序。模式易于理解和索引,因为每个实体都有自己的集合。重要实体不会隐藏在其他文档中。

我很想知道你的发现。祝你好运!