繁体   English   中英

如何在 mongo db 中优化此查询?

[英]How can I optimize this query in mongo db?

这是查询:

    const tags = await mongo
      .collection("positive")
      .aggregate<{ word: string; count: number }>([
        {
          $lookup: {
            from: "search_history",
            localField: "search_id",
            foreignField: "search_id",
            as: "history",
            pipeline: [
              {
                $match: {
                  created_at: { $gt: prevSunday.toISOString() },
                },
              },
              {
                $group: {
                  _id: "$url",
                },
              },
            ],
          },
        },
        {
          $match: {
            history: { $ne: [] },
          },
        },
        {
          $group: {
            _id: "$word",
            url: {
              $addToSet: "$history._id",
            },
          },
        },
        {
          $project: {
            _id: 0,
            word: "$_id",
            count: {
              $size: {
                $reduce: {
                  input: "$url",
                  initialValue: [],
                  in: {
                    $concatArrays: ["$$value", "$$this"],
                  },
                },
              },
            },
          },
        },
        {
          $sort: {
            count: -1,
          },
        },
        {
          $limit: 50,
        },
      ])
      .toArray();

我想我需要一个索引,但不确定如何或在哪里添加。

在我们确认该方法本身是合理的满足所需的应用程序逻辑之后,也许应该重新审视该操作的性能。

在性能方面,如果目的是处理每个文档,则无法提高positive收集的效率。 根据定义,处理所有文档需要完整的集合扫描。

为了有效地支持search_history集合上的$lookup ,您可能希望确认{ search_id: 1, created_at: 1, url: 1 }上的索引存在。 提供.explain("allPlansExecution") output 将使我们能够更好地了解当前的性能特征。

所需逻辑

更新问题以包含有关架构和聚合目的的详细信息对于理解整体情况非常有帮助。 仅查看聚合,它似乎正在执行以下操作:

  • 对于positive集合中的每个文档,添加一个名为history的新字段。
  • 这个新字段是来自search_history集合的url值的列表,其中相应的文档具有匹配的search_id值并且是在上created_at之后创建的。
  • 然后聚合过滤以仅保留新history字段具有至少一个条目的文档。
  • 下一阶段然后按word将结果组合在一起。 这里使用了$addToSet运算符,但它可能会生成一个 arrays 数组,而不是去重复的url数组。
  • 聚合的最后 3 个阶段似乎侧重于计算url的数量,并按按该大小降序排序的word返回前50结果。

这是你想要的吗? 特别是以下方面可能值得确认:

  • 您是否打算处理positive集合中的每个文档? 可能是这种情况,但如果没有任何模式/用例上下文,就无法判断。
  • url的尺寸计算是否正确? 在为$group执行$addToSet而不是为后续的$project使用$reduce时,您似乎可能需要使用$map

最好的办法是限制传递到每个阶段的文档数量。 只有在匹配时,mongo 才会在聚合中使用索引,最大使用 1 个索引。

所以最好的办法是在一个非常严格的索引字段上进行匹配。

此外,请注意$limit$skip$sample不是万能的,因为它们仍然会扫描整个集合。

一种有效限制第一阶段选择的文档数量的方法是使用“分页”。 你可以让它像这样工作:

每 X 请求一次

  1. 计算集合中的文档数
  2. 把它分成 Yk max 的块
  3. 使用跳过和限制在 Y、2Y、3Y 等位置查找文档的 _id
  4. 将结果缓存在 redis/memcache 中(或者如果你真的不能这样做,则作为全局变量)

每一个请求

  1. 通过读取 redis used的密钥和nbChunks获取当前要扫描的块
  2. 获取redis中缓存的_ids,分别用于分隔下一个聚合id:${used%nbChunks}id:${(used%nbChunks)+1}
  3. 使用带有 _id 的$match进行聚合_id:{$gte: ObjectId(id0), $lt: ObjectId(id1)}) }
  4. used增量,如果used > X则更新块

进一步优化

如果使用 redis,请在每个键后面加上${cluster.worker.id}:以避免热键。

笔记

  1. 设置块的步骤 3) 可能是一个非常漫长而密集的过程,因此仅在必要时进行,假设每个 X~1k 请求。
  2. 如果您正在扫描最后一个块,请不要将$lt
  3. 一旦这个过程实施,你的工作就是找到适合你需要的 X 和 Y 的最佳位置,受制于 Y 足够大以检索最大文档同时不太长,并且 X 保持块大致等于集合有越来越多的文件。
  4. 这个过程实现起来有点长,但是一旦实现,时间复杂度是~O(Y)而不是~O(N)。 事实上, $match是第一阶段,_id 是一个被索引的字段,这个第一阶段非常快,并且限制了扫描的最大 Y 个文档。

希望它有所帮助=)如果需要,请务必询问更多=)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM