MongoDB简单聚合,提高了查找性能

relj7zay  于 2023-03-17  发布在  Go
关注(0)|答案(1)|浏览(131)

所以我在mongoDB中有以下查询:

[
  {
    $project: {
      _id: 0,
      id: 1,
      uniqueId: 1,
      shortLabel: 1,
      "geographicLocation.id": 1,
      "economicConcept.id": 1,
      "frequency.id": 1,
      "scale.id": 1,
      "unit.id": 1,
      source: { $arrayElemAt: ["$source", 0] },
    },
  },
  {
    $lookup: {
      from: "geographic_location",
      localField: "geographicLocation.id",
      foreignField: "id",
      as: "region",
    },
  },
  {
    $match: {
      $and: [
        {
          $or: [
            { "region.name": "United States" },
          ],
        },
      ],
    },
  },
  {
    $facet: {
      metadata: [
        { $count: "total" },
        { $addFields: { page: 1 } },
      ],
      data: [{ $skip: 0 }, { $limit: 10 }],
    },
  },
]

然而,对于D(200k)中的记录量来说,它相当慢。我正在尝试改进这个查询,但我不确定为什么方法不起作用。
我已经在运行查找的geographic_location集合上的id上添加了索引。但它似乎没有使用此索引,而是使用COLSCAN策略。Match阶段也太慢,但我再次放置了索引。
下面是查询的解释结果的外观:

{
  "stages": [
    {
      "$cursor": {
        "queryPlanner": {
          "plannerVersion": 1,
          "namespace": "ihs_markit_data.unique_ids_in_each_bank",
          "indexFilterSet": false,
          "parsedQuery": {},
          "queryHash": "57975262",
          "planCacheKey": "57975262",
          "winningPlan": {
            "stage": "PROJECTION_DEFAULT",
            "transformBy": {
              "uniqueId": true,
              "shortLabel": true,
              "id": true,
              "geographicLocation": {
                "id": true
              },
              "_id": false
            },
            "inputStage": {
              "stage": "COLLSCAN",
              "direction": "forward"
            }
          },
          "rejectedPlans": []
        },
        "executionStats": {
          "executionSuccess": true,
          "nReturned": 230045,
          "executionTimeMillis": 12134,
          "totalKeysExamined": 0,
          "totalDocsExamined": 230045,
          "executionStages": {
            "stage": "PROJECTION_DEFAULT",
            "nReturned": 230045,
            "executionTimeMillisEstimate": 331,
            "works": 230047,
            "advanced": 230045,
            "needTime": 1,
            "needYield": 0,
            "saveState": 254,
            "restoreState": 254,
            "isEOF": 1,
            "transformBy": {
              "uniqueId": true,
              "shortLabel": true,
              "id": true,
              "geographicLocation": {
                "id": true
              },
              "_id": false
            },
            "inputStage": {
              "stage": "COLLSCAN",
              "nReturned": 230045,
              "executionTimeMillisEstimate": 70,
              "works": 230047,
              "advanced": 230045,
              "needTime": 1,
              "needYield": 0,
              "saveState": 254,
              "restoreState": 254,
              "isEOF": 1,
              "direction": "forward",
              "docsExamined": 230045
            }
          },
          "allPlansExecution": []
        }
      },
      "nReturned": 230045,
      "executionTimeMillisEstimate": 360
    },
    {
      "$lookup": {
        "from": "geographic_location",
        "as": "region",
        "localField": "geographicLocation.id",
        "foreignField": "id"
      },
      "nReturned": 230045,
      "executionTimeMillisEstimate": 11840
    },
    {
      "$match": {
        "region.name": {
          "$eq": "United States"
        }
      },
      "nReturned": 114639,
      "executionTimeMillisEstimate": 12004
    },
    {
      "$facet": {
        "metadata": [
          {
            "$teeConsumer": {},
            "nReturned": 114639,
            "executionTimeMillisEstimate": 12123
          },
          {
            "$group": {
              "_id": {
                "$const": null
              },
              "total": {
                "$sum": {
                  "$const": 1
                }
              }
            },
            "nReturned": 1,
            "executionTimeMillisEstimate": 12134
          },
          {
            "$project": {
              "total": true,
              "_id": false
            },
            "nReturned": 1,
            "executionTimeMillisEstimate": 12134
          },
          {
            "$addFields": {
              "page": {
                "$const": 1
              }
            },
            "nReturned": 1,
            "executionTimeMillisEstimate": 12134
          }
        ],
        "data": [
          {
            "$teeConsumer": {},
            "nReturned": 10,
            "executionTimeMillisEstimate": 0
          },
          {
            "$limit": 10,
            "nReturned": 10,
            "executionTimeMillisEstimate": 0
          }
        ]
      },
      "nReturned": 1,
      "executionTimeMillisEstimate": 12134
    }
  ],
  "serverInfo": {
    "host": "",
    "port": ,
    "version": "4.4.0",
    "gitVersion": ""
  },
  "ok": 1
}

你知道我的问题出在哪里吗?

ttp71kqs

ttp71kqs1#

您正在尝试提高此操作的性能,但您的目标是什么?您是希望将时间缩短10%、满足特定SLA还是其他要求?此操作运行的频率是多少?数据是否会继续增长?
总的来说,这里没有足够的信息来充分回答这个问题。也就是说,我们仍然可以做出一些观察和指示。尽管如此,我怀疑这里可能存在一个架构问题,而且似乎不太可能使用当前的方法来实现您想要的性能。
在较高的层次上,下面是数据库执行此聚合所做的工作:

  • 对源unique_ids_in_each_bank集合中的230,045文档执行完整集合扫描。这是因为您没有以任何方式限制来自原始集合的数据。这就是您在explain输出中看到对COLLSCAN的引用的原因。这些文档中的每一个都通过$project阶段进行转换,然后传递到后续阶段进行进一步处理。
  • 对于每个输入文档(230,045次),在geographic_location集合中执行$lookup。对于上面的示例查询,$lookup操作 * 可以 * 使用{ id: 1, name: 1 }上的复合索引,但目前还不清楚这是否会以任何有意义的方式改变业绩。在较新版本的数据库中,$lookupexplain输出中有改进的度量。
  • $match阶段之后,大约一半(114,639)的原始文档仍然属于结果集,并被传递以供进一步处理。
  • $facet的两个子管道之一正在计数所有传入文档。因此,所有114,639文档将流入此阶段并在它将结果(计数和前10项)返回给客户端之前进行处理。
  • 还要注意,另一个子管道正在执行skip + limit来执行分页,随着数据集的增长,分页本身可能会很慢且效率低下。

看起来数据库需要大约12秒来扫描整个集合,在另一个集合中进行230k次查找,然后处理所有这些结果。这是一个需要做的大量工作,而且确实没有索引或其他有意义的东西来改变这一点。
需要考虑的一些潜在事项:

  • 创建上面提到的复合指数,尽管这充其量可能只会带来微小的改善。
  • 为计数和检索结果发出单独的查询。虽然计数仍然很慢,但检索结果(的第一页)的查询将不再具有阻塞阶段,并且返回速度应该快得多。随着skip值的增大,这将变慢。
  • 更改您的架构。将此geographic_location信息合并到源集合中的文档中是否很常见?如果是这样,则强制数据库在每次读取数据时生成新文档可能是一个沉重的负担。

最后,请记住,没有(唯一)排序的分页可能会导致意外的结果。

相关问题