【问题标题】:Mongo optimisation: query performance vs database structureMongo 优化:查询性能与数据库结构
【发布时间】:2018-09-07 11:16:43
【问题描述】:

上下文:连接到 MongoDB 4.0 服务器的 NodeJs (meteorjs) 应用程序

我有一组我经常计算的数据,我需要存储更多时间,然后从我的应用程序访问一组特定的数据。该集合是一个包含 12 000 个对象的数组,其最终权重约为 3MB(通过对只有一组数据的集合使用 MongoDB 集合统计信息测量:大小:3,3MB;计数:12964)。它与一些计算参数有关。我需要使用查询来检索集合。

我必须在两种数据库结构之间进行选择:

选项 1: 一个集合存储带有 ID 的计算引用(我们将其命名为 ReferenceCollection),另一个集合每个计算的所有 12000 个对象存储为单个文档,并且 referenceId 指向之前创建的 ID。

这是一个示意图:

ReferenceCollection :
|--- _id: ObjectId("a")
|--- computation : "my reference"

ResultCollection : 
|--- _id: ObjectId("b")
|--- referenceId : ObjectId("a")
|--- fieldResut1 : data
.
.
|--- fieldResut20 : data

要检索该集合,我将使用计算参数在第一个集合中查询 referenceId),然后使用引用 Id 查询第二个集合以获取 12 000 个文档。

let reference = ReferenceCollection.findOne({computation: "my reference"}) // this is lightweight
let results = ResultCollection.find({referenceId: reference._id}) // this search for the 12 000 results

选项 2: 存储计算引用的单个集合,其中包含一个包含数据的数组的键

这是一个示意图:

ResultCollection : 
|--- _id: ObjectId("b")
|--- computation : "my reference"
|--- result : Array(    
    |--- fieldResut1 : data
    .
    .
    |--- fieldResut20 : data
)

要检索该集合,我将只使用我的计算参数进行一次查询,以获取包含我所有数据的单个文档。

问题: 我遇到了第一个选项的性能问题:从 MongoDB 桌面客户端(studio 3T)查询和检索所有 12000 个文档非常慢:在我的设置中需要 3 秒。第二个选项只需 1 秒即可检索(这些时间包括数据的下载)。这会导致我的应用在获取数据时等待很长时间。

在返回游标时从服务器上的 mongoshell 查询非常快(选项 1 大约 20 毫秒)。


您能否确认选项 2 是存储此数据的好选择?

关于数据结构,我还有其他选择吗?

我在单个节点上运行 MongoDB。您认为设置副本集有助于提高读取性能吗?

【问题讨论】:

  • "MongoDB 4.0 连接到 NodeJs" - 反过来。 “最终重量约为 3Mo” - 您使用什么单位来称重? “两个数据库结构” - 解释有点混乱,文档和查询的示例将大大增加获得答案的机会。 “很慢” - 你是$lookup 还是.forEach 呢? “非常快” - 你是检索所有文档还是只是一个光标? “设置副本集有助于提高读取性能” - 绝对不是。
  • 您好,感谢您的评论。我已考虑您的评论并编辑了我的帖子。关于“相当慢”=> 它是在查询和检索所有数据以显示它时,我猜想像 .toArray() 函数。关于“非常快”,它是一个光标。您能详细说明为什么副本集不会提高读取性能吗?在我看来,副本集将允许并行读取,从而提高性能。谢谢
  • 3,3Mo 是否意味着 3.3MB?默认情况下,集合统计以字节为单位返回数据大小,比例为 1。大小对于选项 2 至关重要。如果我了解您想要嵌入 12,000 个文档 x 20 个字段。 mongodb 中单个文档的大小限制为 16 MB,包括字段名称 + 可忽略的开销。如果你的“数据”是一个数字或几个字节的字符串,它有点适合 3.3 MB。在这种情况下,您会在数据库之外的某个地方浪费时间 - 网络、应用程序等。文档结构的更改将无济于事
  • 你在ResultCollection.referenceId 上有一个索引,不是吗?
  • 是的,我的意思是 3,3MB,抱歉。 ResultCollection.referenceId 已编入索引。我认为文档结构会产生影响,因为我看到了性能上的巨大差异(1 秒与 3 秒:很多)。当然网络会造成延迟,但是这里两个结构的下载时间应该是一样的。

标签: database mongodb performance optimization data-structures


【解决方案1】:

在这种情况下您可能会发现差异主要是由于必须执行两个连接/查询,这主要与您的网络相关。

选项 1,例如,如果在交易集合中引用的用户集合。

这个想法是,如果您需要连接两个集合,则只有在连接的集合将被多次引用并且具有复杂文档时才这样做。

如果只是有一个名称集合,然后在另一个集合中引用,那就错了。

如果您需要连接两个 Mongo 集合,请考虑使用聚合,以便 Mongo 服务器可以在 1 次点击中获取数据,而不是需要执行多个查询。

编辑:

为了让您了解性能,目前的第一个选项将花费两倍的时间,纯粹是因为它必须连接两次。如果相同的查询经常发生,那么您真的会看到性能受到无益的影响(除非“计算”字段发生很大变化,那么这可能证明它是合理的)。如果您使用聚合,则不会真正看到任何性能损失,因为它被视为单个连接。

选项二只是一次查找,然后是返回数组数据所需的时间。所以在大多数情况下,使用聚合时与选项 1 相同。

如果数组包含复杂的对象,还要考虑到它可能是一个瓶颈。理想情况下,您会避免使用数组并将其展平为带有字段的单个文档。这样,当您进行查询时,您可以设置要返回的字段,从而不会返回不需要的字段。

【讨论】:

  • 感谢您的回答,它确认选项 2 是我的数据的良好理论选择(无需将此数据链接到其他内容)。您能否为我的两个用例添加一些关于查询性能的见解?
  • 更新了我的答案。确定的最佳方法是加载两个选项,最多包含 50 个,也许是 100,000 个文档,然后对这两个选项重复查询几千个查询,看看哪个先完成。主要规则是保持简单,只返回你需要的数据。这两个规则都应该减少服务器负载和数据传输,这显然减少了等待时间。编辑:还要记住多个用户使用它以及复杂查询可能导致的潜在 CPU 使用率。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-06-14
  • 1970-01-01
  • 2011-11-30
  • 2011-02-27
  • 1970-01-01
  • 2018-07-24
  • 1970-01-01
相关资源
最近更新 更多