题 MongoDB:文档大小会影响查询性能吗?


假设一个由包含a的MongoDB数据库支持的移动游戏 User 收集了数百万份文件。

现在假设必须与用户相关联的几十个属性 - 例如一个数组 _id 价值观 Friend 文件,用户名,照片,数组 _id 价值观 Game 文件,last_login日期,游戏货币数量等,等等。

我担心的是,在数百万个用户文档上创建和更新大型增长数组是否会给每个用户文档增加任何“权重”,和/或整个系统的速度变慢。

我们可能永远不会超过每个文档16mb,但我们可以肯定地说,如果我们直接存储这些增长列表,我们的文档将增加10-20倍。

问题:这是MongoDB中的一个问题吗?如果使用投影和索引等正确管理您的查询,文档大小是否重要?我们是否应该积极修剪文档大小,例如引用外部列表与嵌入列表 _id 值直接?

换句话说:如果我想要一个用户 last_login 值,将是一个只投影/选择的查询 last_login 如果我的话,字段会有所不同 User 文件是100kb对5mb?

或者:如果我想查找具有特定用户的所有用户 last_login 值,文档大小会影响那种查询吗?


16
2018-05-23 20:31


起源




答案:


首先,您应花一点时间阅读MongoDB如何参考填充因子和powerof2sizes分配来存储文档:

http://docs.mongodb.org/manual/core/storage/ http://docs.mongodb.org/manual/reference/command/collStats/#collStats.paddingFactor

简单地说,MongoDB尝试在存储原始文档时分配一些额外的空间以允许增长。 Powerof2sizes分配成为2.6版本中的默认方法,它将以2的幂增长文档大小。

总的来说,如果所有更新都符合原始大小分配,性能会更好。原因是,如果他们不这样做,整个文档需要在其他地方移动,并留有足够的空间,从而导致更多的读写操作,从而实际上破坏了存储。

如果您的文档的大小实际上会增加10倍到20倍的加班时间,这可能意味着每个文档有多次移动,这取决于您的插入,更新和读取频率可能会导致问题。如果是这种情况,您可以考虑以下几种方法:

1)在初始插入时分配足够的空间以覆盖正常文档生命周期增长的大部分(假设90%)。虽然这在开始时的空间使用效率很低,但随着文档的增长,效率将随着时间的推移而增加而不会降低性能。实际上,您将提前支付存储费用,以便稍后使用,以便随着时间的推移获得良好的性能。

2)创建“溢出”文档 - 假设一个典型的80-20规则适用,80%的文档适合一定的大小。如果他们有超过100个朋友或100个游戏文档,则为该数量分配并添加文档可指向的溢出集合。溢出字段指向此新集合中的文档,如果存在溢出字段,则应用程序仅查找新集合。允许80%的用户进行正常的文档处理,并避免在80%的不需要的用户文档上浪费大量存储,但代价是额外的应用程序复杂性。

在任何一种情况下,我都会考虑通过构建适当的索引来使用覆盖的查询:

覆盖查询是一种查询,其中:

all the fields in the query are part of an index, and
all the fields returned in the results are in the same index.

因为索引“覆盖”了查询,所以MongoDB都可以匹配查询   条件并仅使用索引返回结果; MongoDB可以   不需要查看文档,只需要索引,即可实现   查询。

仅查询索引可以比查询文档快得多   在指数之外。索引键通常小于   他们编目的文档,索引通常在RAM或   顺序位于磁盘上。

更多关于这种方法: http://docs.mongodb.org/manual/tutorial/create-indexes-to-support-queries/


12
2018-05-23 21:55



因此,换句话说,文档的大小对查询的性能没有显着影响 - 但对于更新操作等,它非常重要(以您注意的方式)。那是对的吗? - toblerpwn
我不会那么远:) - 从你的例子中不清楚你的文件有多大。较大的文档可能会导致较低的性能,但它是大小和整体用例的函数。通过频繁更新显着增加文档大小会导致性能影响和低效的存储使用。如果您有大型文档但在任何给定时间内有限的工作字段集,则查询可以产生很大的不同。您可以查看预读设置以优化大型文档检索 docs.mongodb.org/manual/administration/production-notes - John Petrone
我认为它们本身并不大。可能有20个简单属性,其中2个数组存储其他文档ID - 平均每个数组可能有10-20个项目。我想我的后续问题的根源是,在这种环境中:我们是否应该积极地修剪文档大小,例如引用外部列表文档与直接嵌入_id值列表?或者是使用管理良好的查询(覆盖和正确投影/选择)的标准做法,并且在大多数常见情况下不会担心文档大小? - toblerpwn
你的文件对我来说听起来不是很大 - 请记住,限制是16 MB,除非我遗漏了一些你将低于16 KB的东西,所以你不应该担心文档大小。大多数人都不太担心文档大小。通过嵌入的_id值引用外部集合会显着增加查询成本(两个而不是一个),因此您只能在必要时执行此操作。总而言之,我可以为您提供MongoDB的最佳建议是对其进行基准测试。每个用例都有所不同。 - John Petrone
这个答案本身是正确的,但并没有真正回答这个问题。我想知道为什么这是一个公认的答案。 - Mayank Jaiswal


重新解释这个问题的一种方法是,如果文件是16mb而不是16kb,则100万文档查询需要更长时间。

如果我错了,请纠正我,根据我自己的经验,文档大小越小,查询越快。

我已经对500k文档和25k文档进行了查询,25k查询明显更快 - 范围从几毫秒到1-3秒更快。在生产时,时差约为2倍-10倍。

文档大小发挥作用的一个方面是查询排序,在这种情况下,文档大小将影响查询本身是否运行。我已经多次达到这个限制,尝试排序只有2k文件。

这里有一些解决方案更多参考: https://docs.mongodb.org/manual/reference/limits/#operations https://docs.mongodb.org/manual/reference/operator/aggregation/sort/#sort-memory-limit

在一天结束时,它的最终用户会受到影响。

当我尝试修复大型查询时,导致性能无法接受。我经常发现自己创建了一个包含数据子集的新集合,并使用了大量的查询条件以及排序和限制。

希望这可以帮助!


6
2018-01-29 21:01