提高 MongoDB 中的聚合框架性能



我的收藏中有20万条记录。我的数据模型如下所示:

{
    "_id" : ObjectId("51750ec159dcef125863b7c4"),
    "DateAdded" : ISODate("2013-04-22T00:00:00.000Z"),
    "DateRemoved" : ISODate("2013-12-22T00:00:00.000Z"),
    "DealerID" : ObjectId("51750bd559dcef07ec964a41"),
    "ExStockID" : "8324482",
    "Make" : "Mazda",
    "Model" : "3",
    "Price" : 11479,
    "Year" : 2012,
    "Variant" : "1.6d (115) TS 5dr",
    "Turnover": 150
} 

我有几个集合索引,其中一个用于聚合框架是:

{
    "DealerID" : 1,
    "DateRemoved" : -1,
    "Price" : 1,
    "Turnover" : 1
}

正在使用的聚合查询:

db.stats.aggregate([
{
    "$match": {
        "DealerID": {
            "$in": [
                ObjectId("523325ac59dcef1b90a3d446"),
                ....
                // here is specified more than 150 ObjectIds
            ]
        },
        "DateRemoved": {
            "$gte": ISODate("2013-12-01T00:00:00Z"),
            "$lt": ISODate("2014-01-01T00:00:00Z")
        }
    }
},
{ "$project" : { "Price":1, "Turnover":1 } },
{
    "$group": {
        "_id": null,
        "Price": {
            "$avg": "$Price"
        },
        "Turnover": {
            "$avg": "$Turnover"
        }
    }
}]);

此查询执行的时间在 30-200 秒之间。

如何优化?

您可以尝试在聚合管道上运行 explain ,但由于我没有您的完整数据集,因此无法正确尝试:

p = [
{
    "$match": {
        "DealerID": {
            "$in": [
                ObjectId("51750bd559dcef07ec964a41"),
                ObjectId("51750bd559dcef07ec964a44"),
            ]
        },
        "DateRemoved": {
            "$gte": ISODate("2013-12-01T00:00:00Z"),
            "$lt": ISODate("2014-01-01T00:00:00Z")
        }
    }
},
{ "$project" : { "Price":1, "Turnover":1 } },
{
    "$group": {
        "_id": null,
        "Price": {
            "$avg": "$Price"
        },
        "Turnover": {
            "$avg": "$Turnover"
        }
    }
}];
db.s.runCommand('aggregate', { pipeline: p, explain: true } );

我建议删除不属于$match(价格和营业额)的字段。另外,我认为您应该切换DealerId和DateRemoved的顺序,因为您想进行一个范围搜索,然后从该范围包括所有经销商。反过来意味着您实际上只能将索引用于 150 个单个项目,然后您需要进行范围搜索。

使用@Derick的答案,我找到了阻止创建覆盖索引的索引。据我所知,查询优化器使用仅涵盖查询本身的第一个索引,因此我更改了索引的顺序。所以这是之前和之后的结果。

以前:

{
    "serverPipeline" : [ 
        {
            "query" : {...},
            "projection" : { "Price" : 1, "Turnover" : 1, "_id" : 0 },
            "cursor" : {
                "cursor" : "BtreeCursor DealerIDDateRemoved multi",
                "isMultiKey" : false,
                "n" : 11036,
                "nscannedObjects" : 11008,
                "nscanned" : 11307,
                "nscannedObjectsAllPlans" : 11201,
                "nscannedAllPlans" : 11713,
                "scanAndOrder" : false,
                "indexOnly" : false,
                "nYields" : 0,
                "nChunkSkips" : 0,
                "millis" : 58,
                "indexBounds" : {...},
                "allPlans" : [...],
                "oldPlan" : {...},
                "server" : "..."
            }
        }, 
        {
            "$group" : {...}
        }
    ],
    "ok" : 1
}

在这些更改之后indexOnly参数现在显示true,这意味着我们刚刚创建了覆盖索引:

{
    "serverPipeline" : [ 
        {
            "query" : {...},
            "projection" : { "Price" : 1, "Turnover" : 1, "_id" : 0 },
            "cursor" : {
                "cursor" : "BtreeCursor DealerIDDateRemovedPriceTurnover multi",
                "isMultiKey" : false,
                "n" : 11036,
                "nscannedObjects" : 0,
                "nscanned" : 11307,
                "nscannedObjectsAllPlans" : 285,
                "nscannedAllPlans" : 11713,
                "scanAndOrder" : false,
                "indexOnly" : true,
                "nYields" : 0,
                "nChunkSkips" : 0,
                "millis" : 58,
                "indexBounds" : {...},
                "allPlans" : [...],
                "server" : "..."
            }
        }, 
        {
            "$group" : {...}
    ],
    "ok" : 1
}

现在,查询大约在 0.085-0.300 秒之间工作。有关涵盖查询的其他信息 创建支持涵盖查询的索引

最新更新