【问题标题】:Optimise MongoDB aggregate query优化 MongoDB 聚合查询
【发布时间】:2018-04-18 06:46:21
【问题描述】:

我有一个包含数百万个文档的集合,每个文档代表一个事件:{_id, product, timestamp} 在我的查询中,我需要按产品分组并以前 10 名为例。

"aggregate" : "product_events",
    "pipeline" : [
        {
            "$match" : {
                "timeEvent" : {
                    "$gt" : ISODate("2017-07-17T00:00:00Z")
                }
            }
        },
        {
            "$group" : {
                "_id" : "$product",
                "count" : {
                    "$sum" : 1
                }
            }
        },
        {
            "$sort" : {
                "count" : -1
            }
        },
        {
            "$limit" : 10
        }
    ]

我的查询现在很慢(10 秒),我想知道是否有办法以不同的方式存储数据来优化此查询?

db.product_events.explain("executionStats").aggregate([ {"$match" : 
{"timeEvent" : {"$gt" : ISODate("2017-07-17T00:00:00Z")}}},{"$group" : 
{"_id" : "$product","count" : {"$sum" : 1}}}, {"$project": {"_id": 1, 
"count": 1}} , {"$sort" : {"count" : -1}},{"$limit" : 500}], 
{"allowDiskUse": true})
{
"stages" : [
    {
        "$cursor" : {
            "query" : {
                "timeEvent" : {
                    "$gt" : ISODate("2017-07-17T00:00:00Z")
                }
            },
            "fields" : {
                "product" : 1,
                "_id" : 0
            },
            "queryPlanner" : {
                "plannerVersion" : 1,
                "namespace" : "mydb.product_events",
                "indexFilterSet" : false,
                "parsedQuery" : {
                    "timeEvent" : {
                        "$gt" : ISODate("2017-07-17T00:00:00Z")
                    }
                },
                "winningPlan" : {
                    "stage" : "COLLSCAN",
                    "filter" : {
                        "timeEvent" : {
                            "$gt" : ISODate("2017-07-17T00:00:00Z")
                        }
                    },
                    "direction" : "forward"
                },
                "rejectedPlans" : [ ]
            },
            "executionStats" : {
                "executionSuccess" : true,
                "nReturned" : 2127315,
                "executionTimeMillis" : 940,
                "totalKeysExamined" : 0,
                "totalDocsExamined" : 2127315,
                "executionStages" : {
                    "stage" : "COLLSCAN",
                    "filter" : {
                        "timeEvent" : {
                            "$gt" : ISODate("2017-07-17T00:00:00Z")
                        }
                    },
                    "nReturned" : 2127315,
                    "executionTimeMillisEstimate" : 810,
                    "works" : 2127317,
                    "advanced" : 2127315,
                    "needTime" : 1,
                    "needYield" : 0,
                    "saveState" : 16620,
                    "restoreState" : 16620,
                    "isEOF" : 1,
                    "invalidates" : 0,
                    "direction" : "forward",
                    "docsExamined" : 2127315
                }
            }
        }
    },
    {
        "$group" : {
            "_id" : "$product",
            "count" : {
                "$sum" : {
                    "$const" : 1
                }
            }
        }
    },
    {
        "$project" : {
            "_id" : true,
            "count" : true
        }
    },
    {
        "$sort" : {
            "sortKey" : {
                "count" : -1
            },
            "limit" : NumberLong(500)
        }
    }
],
"ok" : 1
}

低于我的索引

db.product_events.getIndexes()
[
{
    "v" : 2,
    "key" : {
        "_id" : 1
    },
    "name" : "_id_",
    "ns" : "mydb.product_events"
},
{
    "v" : 2,
    "key" : {
        "product" : 1,
        "timeEvent" : -1
    },
    "name" : "product_1_timeEvent_-1",
    "ns" : "mydb.product_events"
}
]

【问题讨论】:

  • producttimeEvent 设置一个index,并在explain() 中查看它如何影响您的结果,然后分享您的发现。
  • 是的,我已经有关于 product 和 timeEvent 的索引
  • 然后,请在此处发布explain(executionStats) 结果,否则无法找出问题所在。
  • 是的,我刚刚发布了它
  • executionStats,很明显您的索引没有被用于查询,它执行集合扫描而不是索引扫描。请同时发布getIndexes() 的输出。

标签: mongodb mongodb-query


【解决方案1】:

在集合的字段上创建索引有助于优化从数据库集合中检索数据的过程。

索引通常在根据特定标准过滤数据的字段上创建。

包含在索引字段中的数据按特定顺序排序,一旦找到匹配的数据,就会停止扫描其他文档,从而加快数据获取过程。

根据上述问题中提到的描述以优化聚合查询的性能,请尝试在 timeEvent 字段上创建索引,因为 timeEvent 字段用作聚合管道 $match 阶段的过滤器表达式。

【讨论】:

    【解决方案2】:

    复合索引上的documentation 声明如下。

    db.products.createIndex( { "item": 1, "stock": 1 } )

    复合索引中列出的字段的顺序很重要。 index 将包含对首先按值排序的文档的引用 item 字段的值,并且在 item 字段的每个值中,按以下方式排序 stock 字段的值。

    除了支持在所有索引字段上匹配的查询之外, 复合索引可以支持匹配前缀的查询 索引字段。 即索引支持对item字段的查询为 以及 item 和 stock 字段

    您的product_1_timeEvent_-1 索引如下所示:

    {
        "product" : 1,
        "timeEvent" : -1
    }
    

    这就是为什么它不能用于支持仅过滤 timeEvent 的查询。

    您必须对其进行排序的选项:

    • 翻转索引中字段的顺序
    • 从索引中删除 product 字段
    • 创建一个仅包含 timeEvent 字段的附加索引。
    • (在product 字段中添加一些额外的过滤器,以便使用现有索引)

    请记住,索引的任何创建/删除/修改也可能会影响其他查询。因此,请确保正确测试您的更改。

    【讨论】:

      猜你喜欢
      • 2021-09-03
      • 1970-01-01
      • 1970-01-01
      • 2013-01-18
      • 2010-12-18
      • 2021-05-04
      • 2018-02-18
      • 2010-10-23
      • 2020-08-06
      相关资源
      最近更新 更多