【问题标题】:Slow Query in MongoDB with collection wide aggregate query using group使用组在 MongoDB 中使用集合范围聚合查询的慢查询
【发布时间】:2019-06-05 00:49:17
【问题描述】:

我在使用 NodeJS+Express+MongoDB 开发的 API 上遇到性能问题。

在特定产品上使用 $match 运行聚合时,性能很好,但对于开放式搜索,它真的很慢。

我想在两个列上运行一个组:国家和出口商,然后在国家/地区获取限制为每组 3 个结果的结果。

要求:沿线每个国家/地区的唯一出口商总数 每个国家/地区的任意 3 条记录。

在我的aggregate function 上运行explain() 时,我收到以下关键指针,这些指针表明我的查询速度很慢。如果我错了,请纠正我。

  1. "indexFilterSet": false
  2. "winningPlan": { "stage": "COLLSCAN", "direction": "forward" },

9,264,947 记录运行查询,所用时间约为32 seconds。 我尝试过使用复合索引和单字段索引,但它根本没有帮助,因为我觉得索引没有被使用 $match 为空 {}

下面是我使用 mongoose 驱动程序在 mongoDB 上运行的查询

Model.aggregate([
  {"$match" : query},
  { $group : {_id: {country: "$Country", exporter: "$Exporter"}, id: {$first: "$_id"}, product: { $first: "$Description" }}},
  { $group : {_id: "$_id.country", data: {$push: { id: "$id", company: "$_id.exporter", product: "$product" }}, count:{$sum:1}}},
  { "$sort": { "count": -1 } },
  { 
    $project: { 
      "data": { "$slice": [ "$data", 3 ] },
      "_id": 1,
      "count": 1
    }
  },
]).allowDiskUse(true).explain()

其中,query 是动态构建的,默认情况下为空 {} 用于集合范围的搜索。 索引字段是

  1. 复合索引:{Country: 1, Exporter: 1}

  2. 文字索引:{Description: "text"}

完整的解释()响应:

{
"success": "Successfull",
"status": 200,
"data": {
    "stages": [
        {
            "$cursor": {
                "query": {},
                "fields": {
                    "Country": 1,
                    "Description": 1,
                    "Exporter": 1,
                    "_id": 1
                },
                "queryPlanner": {
                    "plannerVersion": 1,
                    "namespace": "db.OpenExportData",
                    "indexFilterSet": false,
                    "parsedQuery": {},
                    "winningPlan": {
                        "stage": "COLLSCAN",
                        "direction": "forward"
                    },
                    "rejectedPlans": []
                }
            }
        },
        {
            "$group": {
                "_id": {
                    "country": "$Country",
                    "exporter": "$Exporter"
                },
                "id": {
                    "$first": "$_id"
                },
                "product": {
                    "$first": "$Description"
                }
            }
        },
        {
            "$group": {
                "_id": "$_id.country",
                "data": {
                    "$push": {
                        "id": "$id",
                        "company": "$_id.exporter",
                        "product": "$product"
                    }
                },
                "count": {
                    "$sum": {
                        "$const": 1
                    }
                }
            }
        },
        {
            "$sort": {
                "sortKey": {
                    "count": -1
                }
            }
        },
        {
            "$project": {
                "_id": true,
                "count": true,
                "data": {
                    "$slice": [
                        "$data",
                        {
                            "$const": 3
                        }
                    ]
                }
            }
        }
    ],
    "ok": 1
}
}

集合大小:9,264,947 条记录和 10.2 GB

响应时间:32154 毫秒

随着我的集合大小的增加,查询变得越来越慢。

【问题讨论】:

    标签: node.js mongodb mongoose aggregate


    【解决方案1】:

    如果您的查询{},mongo 引擎会跳过$match 阶段并直接进入$group。不会使用索引。您可以从explain() 结果中验证上述内容。 $match$sort 管道运算符可以在管道开始时利用索引。查看您的管道,您使用 CountryExporter 对它们进行分组。您可以做的是在{Country: 1, Exporter: 1} 上创建一个索引,并在{Country: 1, Exporter: 1} 上使用$sort 作为管道的第一阶段。这将使$group 更有效率。

    【讨论】:

    • 是的,这是有道理的。但是,在我为国家和出口商编制索引的地方,排序也没有帮助。事实证明,现在这是不可避免的,我们不会向最终用户提供此功能,他们可以在其中查看整体记录的摘要,因为这将是一个巨大的集合。感谢所有信息家伙!干杯!
    【解决方案2】:

    像这样使用聚合意味着 mongodb 必须遍历所有记录,然后对数据进行分组(加载 10 Gb),然后对它会创建的数组进行切片。

    你的收藏增长得越多,它就越长。


    我认为与其优化您的实际请求,不如重新考虑您的方法。


    我会首先使用一个请求find 每个国家/地区名称。然后对每个 country 使用一个请求,以获得前 3 个 exporter

    使用 countryexporter 的索引。

    请求更多,但请求更小,不需要加载所有数据。使用适当的索引直接访问数据。

    考虑到那里没有成千上万个不同的国家

    【讨论】:

    • 这里的核心要求之一是找到每个国家独特出口商的摘要,我觉得在国家和出口商上运行组将是必需的。请让我知道是否有其他方法可以做到这一点。谢谢!
    • 他们是否可以在这些字段上使用已定义的索引以及 group 聚合?
    猜你喜欢
    • 2015-09-05
    • 1970-01-01
    • 2020-10-18
    • 2015-11-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-03-17
    • 1970-01-01
    相关资源
    最近更新 更多