提高MongoDb中聚合框架的性能
我收藏了20万张唱片。我的数据模型如下所示:提高MongoDb中聚合框架的性能,mongodb,Mongodb,我收藏了20万张唱片。我的数据模型如下所示: { "_id" : ObjectId("51750ec159dcef125863b7c4"), "DateAdded" : ISODate("2013-04-22T00:00:00.000Z"), "DateRemoved" : ISODate("2013-12-22T00:00:00.000Z"), "DealerID" : ObjectId("51750bd559dcef07ec964a41"), "ExS
{
"_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个单个项目使用索引,然后您需要进行范围搜索。您可以尝试在聚合管道上运行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个单个项目使用索引,然后您需要进行范围搜索。您可以尝试在聚合管道上运行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个单个项目使用索引,然后您需要进行范围搜索。您可以尝试在聚合管道上运行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
param现在显示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秒之间工作。关于覆盖查询的其他信息使用@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
param现在显示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秒之间工作。关于覆盖查询的其他信息使用@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
param现在显示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秒之间工作。关于覆盖查询的其他信息使用@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
param现在显示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秒之间工作。有关覆盖查询的其他信息这里只是一个测试,如果删除$avg运算符,速度快吗?很遗憾,您不能在聚合上使用
.explain()
。但是,您可以将$match作为一个普通的集合来执行。查找,然后查找.explain()
,以获得一个非常有用的解释输出,告诉您索引是否有效。$project和$group没有从索引中受益,因此当您特别为这个查询创建索引时,它可能会工作得更好(或者至少也一样好)如果没有价格
和营业额
@Philipp,您实际上可以使用explain,它就无法跨平台工作sharding@Philipp无意争辩,只是想知道为什么$project donesn不能从该指数中获益?看起来Miro试图在这里创建一个覆盖索引$project应该使用索引来避免查找实际文档。如果删除$avg运算符,则此处需要测试。很遗憾,您不能在聚合上使用.explain()
。但是,您可以将$match作为一个普通的集合来执行。查找,然后查找.explain()
,以获得一个非常有用的解释输出,告诉您索引是否有效。$project和$group没有从索引中受益,因此当您特别为这个查询创建索引时,它可能会工作得更好(或者至少也一样好)没有价格
和营业额