了解MongoDB BSON文档大小限制

168

来自 MongoDB权威指南:

当一个文档转换为BSON后大于4MB时,不能保存到数据库中。这是一个相对任意的限制(并且在未来可能会提高),主要是为了防止不良模式设计和保证一致的性能。

我不理解这个限制,这是否意味着包含很多评论的博客文章等大于4MB的文档不能作为单个文档存储?

这是否也适用于嵌套文档?

如果我想要一个记录值更改的文档(它可能会随着时间增长而超过4MB限制),该怎么办?

希望有人能正确解释这个问题。

我刚开始学习 MongoDB (第一个学习的 NoSQL 数据库)。

谢谢。


6
我认为问题应该澄清这是MongoDB存储文档大小的限制,而不是BSON格式的限制。 - alexpopescu
3
我刚刚试图保存一个非常大的文件,肯定超过了4MB,但提示" BSON::InvalidDocument: Document too large: BSON documents are limited to 4194304 bytes." 如果是这样,那么警告/错误信息有点误导人了吧? - Nik So
25
mongo shell中,您可以使用命令 db.isMaster().maxBsonObjectSize/(1024*1024)+' MB' 轻松查找最大的 BSON 文档大小。 - ahmet alp balkan
9
无模式NoSQL的目的在于可以处理不固定或未知的数据结构,同时提供高可扩展性和灵活性。虽然记录大小受到限制,但仍可以使用基于CRUD操作(创建、读取、更新、删除)来管理数据。 - Rizwan Patel
3
我认为最初的引语已经说明了一切……限制是为了防止不良的模式设计。例如,如果您有一个带有许多评论的帖子,您会想要一个博客条目集合和一个评论集合,或者一个更改集合。Mongo / NoSQL的设计允许将大量的东西作为文档网络,但开发人员需要将它们分成有意义的部分。如果没有设置大小限制,会出现其他问题。我认为4mb的限制很好。16mb也不错!但如果我正在编写一个16mb的文档,那就意味着设计上出了其他问题。 - Eyelash
7个回答

139

首先,下一个版本将把这个限制提高到8MB16MB...但我认为,为了让大家了解,来自MongoDB开发者10gen的Eliot表述得最好:

编辑: 大小已经被正式地提高到16MB

那么,在您的博客示例上,4MB实际上很多。例如,“世界大战”的完整未压缩文本只有364k(html):http://www.gutenberg.org/etext/36

如果您的博客文章有那么长,并且有那么多评论,我一个人是不会读的 :)

对于引用通告,如果您给它们分配1MB,您可以轻松拥有超过10k(可能更接近20k)

因此,除非是真正奇怪的情况,否则它将运行良好。在异常情况或垃圾邮件中,无论如何我都不认为您想要一个20mb的对象。我认为无论出于性能考虑还是出于特殊情况的考虑,将引用通告限制为15k左右都是有意义的。或者如果它曾经发生过,至少特别处理一下。

-Eliot

我认为您很难达到这个限制......而且随着时间的推移,如果您升级......您将越来越不必担心。

该限制的主要目的是为了防止您使用服务器上的所有RAM(因为在查询它时需要将文档的所有MB加载到RAM中)。

因此,限制是常见系统上正常可用RAM的某些%......这将逐年增长。

关于在MongoDB中存储文件的注意事项

如果您需要存储大于 16MB 的文档(或文件),可以使用GridFS API,该API会自动将数据分成段并以流式传输返回给您(从而避免了大小限制/内存问题)。

GridFS不是将整个文件存储在单个文档中,而是将文件分成部分或块,并将每个块作为单独的文档存储。

GridFS使用两个集合来存储文件。其中一个集合存储文件块,另一个集合则存储文件元数据。

您可以使用此方法将图像、文件、视频等存储到数据库中,就像在SQL数据库中存储一样。我甚至曾经使用它来存储多GB的视频文件。


4
很棒,你有足够的RAM来存储整个数据库...通常,“工作集”在RAM中,而不是整个数据库(例如我的情况下,我有超过x GB的多个数据库,如果全部加起来会超过我的RAM,但这没关系,因为工作集要小得多)。另外,如果没有限制,你可能会用一个查询将一个800MB的文档加载到RAM中,用另一个查询将一个400k的文档加载到RAM中,这样平衡你的RAM会有点困难等等。因此,“限制”是典型服务器RAM的一定百分比(随着时间增长而增加)。 http://www.mongodb.org/display/DOCS/Checking+Server+Memory+Usage - Justin Jenkins
3
能够将所有东西存储在RAM中非常好,但是需要考虑效率和博客文章的习语。显然,如果已经阅读了文章,则希望该文章保存在内存中。但是,当大多数人不会阅读超过第一页的情况下,您真的希望一篇博客文章的10页评论都保存在内存中吗?当然,如果您的数据库足够小,可以全部放入内存中,则没有问题。但就纯效率而言,如果可以避免占用无用的内存空间,那么您不希望无用的位占据内存空间(这也适用于关系型数据库)。 - AlexGad
62
天啊,那么蒙戈的论点是“16 MB 应该足够任何人使用”?过去从未证明这种说法是错误的。 - Robert Christ
2
这对我来说似乎太糟糕了。Mongo应该对大数据有用,而不应该有这样的限制。在我的项目中,我需要聚合和分组与同一趋势主题相关的推文,这可能会导致超过20000条推文,时间跨度为20小时(并且我的数据库中很可能会有持续超过20小时的趋势)。存储那么多推文并同时存储它们的文本是毁灭性的,在聚合几个小趋势后,最终会在一个大趋势上出现异常。 - Savvas Parastatidis
12
@savvas 为什么要把所有推文放在一个文档中?应该每个推文使用一个文档,将热门话题作为另一个字段放在文档中。在该主题字段上放置索引,然后使用Mongo管道对该字段进行聚合。与关系型数据库不同,使用NoSQL需要调整方法和思维方式,一旦调整完毕,您会发现它非常适用于许多大数据情境。 - schmidlop
显示剩余6条评论

39

社区中的许多人希望没有限制,但会收到性能警告。请参考此评论以获取充分论据: https://jira.mongodb.org/browse/SERVER-431?focusedCommentId=22283&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-22283

我的看法是,主要开发人员在早期决定了这是一个重要的“功能”,因此他们对这个问题非常固执。由于受到质疑而感到受伤,他们不会很快改变这种做法。这是开源社区中个性和政治干扰产品的另一个例子,但这并不是一个致命问题。


6
我完全同意你的看法,而且这也违背了现在嵌入式文档的初衷,因为大多数嵌入式文档现在很容易超出限制。尤其是其中包含了一系列文档的情况。 - Sharjeel Ahmed
@marr75,它说已经修复了,它已经被修复了吗? - Mafii
1
我的意思是,将限制提高到16MB并不能从长远来看解决这个“问题”;在我看来,应该直接取消限制。 - marr75
2
6年前的线程复活。我对你提出的特定的糟糕用例/设计示例仍然不信服。此外,那个例子更好地说明了为什么需要验证输入,而不是单个文档大小限制。让应用程序将其嵌套文档拆分为另一个集合中的单独文档或启动新的“连续”文档(这些解决方案我已经多次使用以在此限制内工作)对性能影响很小,但对代码复杂性有很大影响。文档数据库的整个重点是数据局部性。 - marr75
6
感谢您对MongoDB文档进行了与之相同的数学计算以支持这个决定,但是您的单一用例和思想实验远非定论。我不得不设计复杂且冗余的方案来解决Mongo存在任意限制(而没有深度嵌套或重复条目)的问题。按照您的逻辑,没有数据库应该包含超过16MB的内容,因为某些任意文本可以使用更少的存储空间进行表示。这显然是荒谬的。 - marr75
显示剩余2条评论

37

为了向那些通过Google被引导到这里的人发布澄清答案。

文档大小包括文档中的所有内容,包括子文档、嵌套对象等。

因此,一个文档的大小为:

{
  "_id": {},
  "na": [1, 2, 3],
  "naa": [
    { "w": 1, "v": 2, "b": [1, 2, 3] },
    { "w": 5, "b": 2, "h": [{ "d": 5, "g": 7 }, {}] }
  ]
}

最大大小为16 MB。

子文档和嵌套对象都计入文档的大小。


1
BSON 中能够表示的最大结构,具有讽刺意味的是也是最紧凑的。尽管 MongoDB 在内部使用 size_t(64 位)数组索引,但 16MB 的文档大小限制最多只能表示一个包含两百万个 NULL 的数组本身的文档。 - amcgregor
1
抱歉,添加第二条评论以解决/澄清另一个重要细节:当您说“文档大小包括文档中的所有内容”时,这也包括“键”。例如 {"f": 1}{"foo": 1} 小两个字节。如果不小心处理,这可能会快速累加,尽管现代磁盘上的压缩有所帮助。 - amcgregor

6

6
我尚未见过限制问题不涉及存储在文件中的大型文件。已经有各种数据库非常有效地存储/检索大型文件;它们称为操作系统。数据库存在于操作系统上方的层。如果您出于性能原因使用NoSQL解决方案,为什么要将DB层放在应用程序和数据之间,从而增加访问数据的额外处理开销呢?
JSON是一种文本格式。因此,如果通过JSON访问数据,则特别适用于具有二进制文件的情况,因为它们必须以uuencode、十六进制或Base 64进行编码。转换路径可能如下所示
二进制文件 <> JSON(已编码)<> BSON(已编码)
将数据文件的路径(URL)放入您的文档中,并将数据本身保留为二进制,这将更有效。
如果您真的想将这些长度未知的文件保存在DB中,那么最好将它们放在GridFS中,并避免在访问大型文件时影响并发性。

1
已经有各种高效存储/检索大文件的数据库存在,它们被称为操作系统。 - redcalx

2
根据https://www.mongodb.com/blog/post/6-rules-of-thumb-for-mongodb-schema-design-part-1
如果您预计博客文章可能超出16Mb的文档限制,则应将评论提取到单独的集合中,并从评论中引用博客文章并执行应用程序级联接。
// posts
[
  {
    _id: ObjectID('AAAA'),
    text: 'a post',
    ...
  }
]

// comments
[
  {
    text: 'a comment'
    post: ObjectID('AAAA')
  },
  {
    text: 'another comment'
    post: ObjectID('AAAA')
  }
]

1
也许将博客文章 -> 评论关系存储在非关系型数据库中并不是最佳设计。

无论如何,您应该将评论单独存储在一个集合中,而不是与博客文章一起存储。

[编辑]

请参见下面的评论以获取进一步讨论。


15
我完全不同意。在你的博客文章中,MongoDB中的评论应该是完全可以接受的......这是非常常见的用法(我在生产环境中的一个或多个地方都使用它,并且它运行得非常好)。 - Justin Jenkins
2
我的回答可能过于严格了。在MongoDB或类似的数据库中存储博客文章和相关评论并没有什么问题。更多的是人们倾向于过度使用基于文档的数据库所提供的能力(最激进的例子是将所有数据存储在名为“blog”的单个文档中)。 - Mchl
3
“博客”这个词不太合适,但将评论存储在单独的集合中出于相同原因也不太好。带有评论数组的帖子就像是文档数据库的典型示例。 - Matt Briggs
7
将评论储存在帖子中就像是文档导向数据库的典型例子(例如将整个维基文本存储在一个文档中)。如果我要编写Stack Overflow,它将完全运行在MongoDB上。这些SO条目没有一个会合理地超过4MB。Craigslist正在将其历史记录进行大规模的数据库迁移,转移到MongoDB上。他们只有几篇文章超过了这个限制,首席开发人员认为这些文章本身实际上是有问题的(结果是一些错误造成的)。再次说明,4MB相当于几部小说的文本量。 - Gates VP
3
@盖茨副总裁,我同意使用单独的全文检索引擎。我在考虑元数据搜索。如果您有一组书籍文档,并且想要查找所有1982年出版的书籍,如果每本书都有100kb以上的文本,则不希望传输多个兆字节来显示前20本书的标题。 - mikerobi
显示剩余8条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接