簡體   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