Mongo 未选择复合索引
Mongo compound index is not chosen
我有以下架构:
{
score : { type : Number},
edges : [{name : { type : String }, rank : { type : Number }}],
disable : {type : Boolean},
}
我已尝试为以下查询构建 index:
db.test.find( {
disable: false,
edges: { $all: [
{ $elemMatch:
{ name: "GOOD" } ,
},
{ $elemMatch:
{ name: "GREAT" } ,
},
] },
}).sort({"score" : 1})
.limit(40)
.explain()
先试试
当我创建 index name "score" :
{
"edges.name" : 1,
"score" : 1
}
'explain'返回:
{
"cursor" : "BtreeCursor score",
....
}
第二次尝试
当我将 "score" 更改为:
{
"disable" : 1,
"edges.name" : 1,
"score" : 1
}
'explain'返回:
"clauses" : [
{
"cursor" : "BtreeCursor name",
"isMultiKey" : true,
"n" : 0,
"nscannedObjects" : 304,
"nscanned" : 304,
"scanAndOrder" : true,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"edges.name" : [
[
"GOOD",
"GOOD"
]
]
}
},
{
"cursor" : "BtreeCursor name",
"isMultiKey" : true,
"n" : 0,
"nscannedObjects" : 304,
"nscanned" : 304,
"scanAndOrder" : true,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"edges.name" : [
[
"GOOD",
"GOOD"
]
]
}
}
],
"cursor" : "QueryOptimizerCursor",
....
}
其中 'name' 索引为:
{
'edges.name' : 1
}
为什么 mongo 拒绝使用索引中的 'disable' 字段?
(除了 'disable' 之外,我也尝试过其他字段,但遇到了同样的问题)
看起来查询优化器正在选择最有效的索引,而 edges.name
上的索引效果最好。我通过根据您的架构插入几个文档来重新创建您的集合。然后我在下面创建了复合索引。
db.test.ensureIndex({
"disable" : 1,
"edges.name" : 1,
"score" : 1
});
当运行对您指定的查询进行解释时,使用了索引。
> db.test.find({ ... }).sort({ ... }).explain()
{
"cursor" : "BtreeCursor disable_1_edges.name_1_score_1",
"isMultiKey" : true,
...
}
但是,一旦我在 edges.name
上添加索引,查询优化器就选择了该索引作为查询。
> db.test.find({ ... }).sort({ ... }).explain()
{
"cursor" : "BtreeCursor edges.name_1",
"isMultiKey" : true,
...
}
不过,如果您希望查询使用复合索引,您仍然可以提示其他索引。
> db.test.find({ ... }).sort({ ... }).hint("disable_1_edges.name_1_score_1").explain()
{
"cursor" : "BtreeCursor disable_1_edges.name_1_score_1",
"isMultiKey" : true,
...
}
[编辑:添加了与 $all
的使用相关的可能解释。]
请注意,如果您 运行 没有 $all
的查询,查询优化器将使用复合索引。
> db.test.find({
"disable": false,
"edges": { "$elemMatch": { "name": "GOOD" }}})
.sort({"score" : 1}).explain();
{
"cursor" : "BtreeCursor disable_1_edges.name_1_score_1",
"isMultiKey" : true,
...
}
我认为这里的问题是您正在使用 $all
。正如您在解释字段的结果中看到的那样,有一些子句,每个子句都与您使用 $all
搜索的值之一有关。因此,查询必须为每个子句找到所有 disable
和 edges.name
对。我的直觉是,这两个带有复合索引的 运行 比直接查看 edges.name
然后清除 disable
的查询慢。这可能与 this issue and this 问题有关,您可能需要查看该问题。
我有以下架构:
{
score : { type : Number},
edges : [{name : { type : String }, rank : { type : Number }}],
disable : {type : Boolean},
}
我已尝试为以下查询构建 index:
db.test.find( {
disable: false,
edges: { $all: [
{ $elemMatch:
{ name: "GOOD" } ,
},
{ $elemMatch:
{ name: "GREAT" } ,
},
] },
}).sort({"score" : 1})
.limit(40)
.explain()
先试试
当我创建 index name "score" :
{
"edges.name" : 1,
"score" : 1
}
'explain'返回:
{
"cursor" : "BtreeCursor score",
....
}
第二次尝试
当我将 "score" 更改为:
{
"disable" : 1,
"edges.name" : 1,
"score" : 1
}
'explain'返回:
"clauses" : [
{
"cursor" : "BtreeCursor name",
"isMultiKey" : true,
"n" : 0,
"nscannedObjects" : 304,
"nscanned" : 304,
"scanAndOrder" : true,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"edges.name" : [
[
"GOOD",
"GOOD"
]
]
}
},
{
"cursor" : "BtreeCursor name",
"isMultiKey" : true,
"n" : 0,
"nscannedObjects" : 304,
"nscanned" : 304,
"scanAndOrder" : true,
"indexOnly" : false,
"nChunkSkips" : 0,
"indexBounds" : {
"edges.name" : [
[
"GOOD",
"GOOD"
]
]
}
}
],
"cursor" : "QueryOptimizerCursor",
....
}
其中 'name' 索引为:
{
'edges.name' : 1
}
为什么 mongo 拒绝使用索引中的 'disable' 字段? (除了 'disable' 之外,我也尝试过其他字段,但遇到了同样的问题)
看起来查询优化器正在选择最有效的索引,而 edges.name
上的索引效果最好。我通过根据您的架构插入几个文档来重新创建您的集合。然后我在下面创建了复合索引。
db.test.ensureIndex({
"disable" : 1,
"edges.name" : 1,
"score" : 1
});
当运行对您指定的查询进行解释时,使用了索引。
> db.test.find({ ... }).sort({ ... }).explain()
{
"cursor" : "BtreeCursor disable_1_edges.name_1_score_1",
"isMultiKey" : true,
...
}
但是,一旦我在 edges.name
上添加索引,查询优化器就选择了该索引作为查询。
> db.test.find({ ... }).sort({ ... }).explain()
{
"cursor" : "BtreeCursor edges.name_1",
"isMultiKey" : true,
...
}
不过,如果您希望查询使用复合索引,您仍然可以提示其他索引。
> db.test.find({ ... }).sort({ ... }).hint("disable_1_edges.name_1_score_1").explain()
{
"cursor" : "BtreeCursor disable_1_edges.name_1_score_1",
"isMultiKey" : true,
...
}
[编辑:添加了与 $all
的使用相关的可能解释。]
请注意,如果您 运行 没有 $all
的查询,查询优化器将使用复合索引。
> db.test.find({
"disable": false,
"edges": { "$elemMatch": { "name": "GOOD" }}})
.sort({"score" : 1}).explain();
{
"cursor" : "BtreeCursor disable_1_edges.name_1_score_1",
"isMultiKey" : true,
...
}
我认为这里的问题是您正在使用 $all
。正如您在解释字段的结果中看到的那样,有一些子句,每个子句都与您使用 $all
搜索的值之一有关。因此,查询必须为每个子句找到所有 disable
和 edges.name
对。我的直觉是,这两个带有复合索引的 运行 比直接查看 edges.name
然后清除 disable
的查询慢。这可能与 this issue and this 问题有关,您可能需要查看该问题。