(如何)聚合()可以打破索引?
(How) can aggregate() break an index?
(如何)此代码可以破坏 MongoDB 集合上的索引?
db.users.aggregate([
{ $group: { _id: null, total_orders: { $sum: { $size: "$orders" } } } },
])
这(显然)试图计算所有用户的所有订单。
我继承了一个代码被注释掉的项目。我需要取消注释(重新启用)代码,但想了解这样做的风险。此更改的作者不可用,他们用于注释掉的提交消息是 "remove statistics calls - breaking index."
我们正在使用的 MongoDB 通过 mlab.com 托管在 AWS 上。我们正在使用猫鼬从 JS 应用程序连接到 mlab。
我怀疑问题不是聚合会破坏索引,而是聚合不使用索引而是会执行集合扫描。
当 $match and/or $sort stages 位于管道的开头时,聚合可以利用索引。此聚合只是一个 $group
阶段,这意味着需要迭代整个集合来计算计数。
我在下面放了一个简单的例子,展示了执行集合扫描的聚合,即使数组字段被索引。
> db.foo.insert({ "x" : [ 1, 2 ] } )
> db.foo.insert({ "x" : [ 1 ] } )
> db.foo.createIndex({ "x" : 1 } )
...
> db.foo.aggregate([ { $group: { _id: null, cnt: { $sum : { $size: "$x" } } } } ] )
{ "_id" : null, "cnt" : 3 }
// Results of a .explain() - see 'winningPlan' below
> db.foo.explain(true).aggregate([ { $group: { _id: null, cnt: { $sum : { $size: "$x" } } } } ] )
{
"stages" : [
{
"$cursor" : {
"query" : {
},
"fields" : {
"x" : 1,
"_id" : 0
},
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "stack.foo",
"indexFilterSet" : false,
"parsedQuery" : {
},
"winningPlan" : {
"stage" : "COLLSCAN",
"direction" : "forward"
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 2,
"executionTimeMillis" : 0,
"totalKeysExamined" : 0,
"totalDocsExamined" : 2,
"executionStages" : {
"stage" : "COLLSCAN",
"nReturned" : 2,
"executionTimeMillisEstimate" : 0,
"works" : 4,
"advanced" : 2,
"needTime" : 1,
"needYield" : 0,
"saveState" : 1,
"restoreState" : 1,
"isEOF" : 1,
"invalidates" : 0,
"direction" : "forward",
"docsExamined" : 2
},
"allPlansExecution" : [ ]
}
}
},
{
"$group" : {
"_id" : {
"$const" : null
},
"cnt" : {
"$sum" : {
"$size" : [
"$x"
]
}
}
}
}
],
"ok" : 1,
...
}
(如何)此代码可以破坏 MongoDB 集合上的索引?
db.users.aggregate([
{ $group: { _id: null, total_orders: { $sum: { $size: "$orders" } } } },
])
这(显然)试图计算所有用户的所有订单。
我继承了一个代码被注释掉的项目。我需要取消注释(重新启用)代码,但想了解这样做的风险。此更改的作者不可用,他们用于注释掉的提交消息是 "remove statistics calls - breaking index."
我们正在使用的 MongoDB 通过 mlab.com 托管在 AWS 上。我们正在使用猫鼬从 JS 应用程序连接到 mlab。
我怀疑问题不是聚合会破坏索引,而是聚合不使用索引而是会执行集合扫描。
当 $match and/or $sort stages 位于管道的开头时,聚合可以利用索引。此聚合只是一个 $group
阶段,这意味着需要迭代整个集合来计算计数。
我在下面放了一个简单的例子,展示了执行集合扫描的聚合,即使数组字段被索引。
> db.foo.insert({ "x" : [ 1, 2 ] } )
> db.foo.insert({ "x" : [ 1 ] } )
> db.foo.createIndex({ "x" : 1 } )
...
> db.foo.aggregate([ { $group: { _id: null, cnt: { $sum : { $size: "$x" } } } } ] )
{ "_id" : null, "cnt" : 3 }
// Results of a .explain() - see 'winningPlan' below
> db.foo.explain(true).aggregate([ { $group: { _id: null, cnt: { $sum : { $size: "$x" } } } } ] )
{
"stages" : [
{
"$cursor" : {
"query" : {
},
"fields" : {
"x" : 1,
"_id" : 0
},
"queryPlanner" : {
"plannerVersion" : 1,
"namespace" : "stack.foo",
"indexFilterSet" : false,
"parsedQuery" : {
},
"winningPlan" : {
"stage" : "COLLSCAN",
"direction" : "forward"
},
"rejectedPlans" : [ ]
},
"executionStats" : {
"executionSuccess" : true,
"nReturned" : 2,
"executionTimeMillis" : 0,
"totalKeysExamined" : 0,
"totalDocsExamined" : 2,
"executionStages" : {
"stage" : "COLLSCAN",
"nReturned" : 2,
"executionTimeMillisEstimate" : 0,
"works" : 4,
"advanced" : 2,
"needTime" : 1,
"needYield" : 0,
"saveState" : 1,
"restoreState" : 1,
"isEOF" : 1,
"invalidates" : 0,
"direction" : "forward",
"docsExamined" : 2
},
"allPlansExecution" : [ ]
}
}
},
{
"$group" : {
"_id" : {
"$const" : null
},
"cnt" : {
"$sum" : {
"$size" : [
"$x"
]
}
}
}
}
],
"ok" : 1,
...
}