优化MongoDB聚合查询

时间:2018-04-17 14:21:25

标签: mongodb mongodb-query

我有一个包含数百万个文档的集合,每个文档代表一个事件:{_ 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"
}
]

2 个答案:

答案 0 :(得分:0)

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

索引通常是在根据特定条件过滤数据的字段中创建的。

索引字段中包含的数据按特定顺序排序,一旦找到匹配的数据,就会停止扫描其他文档,这样可以更快地获取数据。

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

答案 1 :(得分:0)

复合索引上的documentation表示以下内容。

  

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

     

复合索引中列出的字段的顺序很重要。   index将包含对首先按值排序的文档的引用   项目字段的内容,并在项目字段的每个值内排序   股票领域的价值。

     

除了支持所有索引字段匹配的查询外,   复合索引可以支持与前缀匹配的查询   索引字段。 也就是说,索引支持对项目字段的查询为   以及项目和库存字段

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

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

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

您需要对其进行排序的选项:

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

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