我正在运行大量此类类型的查询:
db.mycollection.find({a:{$gt:10,$lt:100}, b:4}).sort({c:-1, a:-1})
我应该使用什么样的索引加快速度?我想我需要同时拥有{a:1, b:1}
和{c:-1, a:-1}
,我是对的吗?或者这些索引会以某种方式相互干扰而不会增加性能?
编辑:对我来说,实际问题是我在一个循环中运行了很多查询,其中一些是小范围的,另一些是大范围的。如果我将索引放在{a:1, b:1}
上,它会非常快速地选择小块,但是当涉及到大范围时,我会看到错误“sort()没有索引的数据太多”。如果,否则,我将索引放在{c:-1, a:-1}
上,没有错误,但较小的块(以及更多的块)处理得慢得多。那么,如何保持较小范围的选择速度,但不会在大量数据上出错?
如果重要,我会通过Python的pymongo运行查询。
答案 0 :(得分:4)
如果你已经阅读了文档,你会发现在这里使用两个索引是没用的,因为MongoDB每个查询只使用一个索引(除非它是$or
),直到:https://jira.mongodb.org/browse/SERVER-3071被实现
不仅如此,而且在使用复合排序时,索引中的顺序必须与正确使用索引的排序顺序相匹配,如下所示:
或者这些索引会以某种方式相互干扰而不会增加性能?
如果实施了交叉,那么{a:1,b:1}
与排序不匹配,而{c:-1,a:-1}
对于回答find()
而言a
加上{a:-1,b:1,c:-1}
不是前缀的次优那个化合物。
因此,最佳索引的迭代将立即:
$gt
但这不是完整的故事。由于$lt
和$in
实际上是范围,例如{{1}}它们会遇到与索引相同的问题,因此本文应该提供答案:http://blog.mongolab.com/2012/06/cardinal-ins/并没有真正看到任何理由重复其内容。
答案 1 :(得分:3)
免责声明:适用于MongoDB v2.4
使用提示是一个不错的解决方案,因为它会强制查询使用您选择的索引,因此您可以使用不同的索引优化查询,直到您满意为止。 缺点是您为每个请求设置自己的索引。 我更喜欢设置整个集合的索引,让Mongo为我选择正确的(最快的)索引,特别是对于重复使用的查询。
您的查询中有两个问题:
.find()
中的文档数量非常大,那么sort()的数据太多而没有索引”,大小取决于mongo的版本你用。这意味着您必须拥有A
和C
的索引才能使查询正常运行。现在出现更大的问题。您正在对param $lt
)执行范围查询($gt
和A
),这不能与Mongo一起使用。 MongoDB一次只使用一个索引,您在同一个参数上使用两个索引。在您的代码中有几种解决方案可以处理它:
r = range( 11,100 )
db.mycollection.find({a:{$in: r }, b:4}).sort({c:-1, a:-1})
在查询中仅使用$lt
或$gt
,
db.mycollection.find({ a: { $lt:100 }, b:4}).sort({c:-1, a:-1})
获取结果并在python代码中过滤它们。
此解决方案将返回更多数据,因此如果您有数百万的结果小于A=11
,请不要使用它!
如果您选择此选项,请确保使用compound key A
和B
。
在您的查询中使用$or
时请注意,因为$或less efficiently optimized比$in
使用索引一样。
答案 2 :(得分:2)
如果你定义一个索引{c:-1,a:-1,b:1},它将有助于你做一些考虑。
使用此选项将完全扫描索引,但根据索引值,将仅访问适当的文档,并且将按正确的顺序访问它们,以便在获得结果后不需要订购阶段。如果索引是巨大的,我不知道它将如何表现,但我假设当结果很小时,如果结果集很大,它会更慢。
关于前缀匹配。如果您提示索引并且较低级别可用于提供查询,则将使用这些级别。为了证明这种行为,我做了一个简短的测试。
我准备了测试数据:
> db.createCollection('testIndex')
{ "ok" : 1 }
> db.testIndex.ensureIndex({a:1,b:1})
> db.testIndex.ensureIndex({c:-1,a:-1})
> db.testIndex.ensureIndex({c:-1,a:-1,b:1})
> for(var i=1;i++<500;){db.testIndex.insert({a:i,b:4,c:i+5});}
> for(var i=1;i++<500;){db.testIndex.insert({a:i,b:6,c:i+5});}
使用提示查询的结果:
> db.testIndex.find({a:{$gt:10,$lt:100}, b:4}).hint('c_-1_a_-1_b_1').sort({c:-1, a:-1}).explain()
{
"cursor" : "BtreeCursor c_-1_a_-1_b_1",
"isMultiKey" : false,
"n" : 89,
"nscannedObjects" : 89,
"nscanned" : 588,
"nscannedObjectsAllPlans" : 89,
"nscannedAllPlans" : 588,
"scanAndOrder" : false,
"indexOnly" : false,
"nYields" : 0,
"nChunkSkips" : 0,
"millis" : 1,
"indexBounds" : {
"c" : [
[
{
"$maxElement" : 1
},
{
"$minElement" : 1
}
]
],
"a" : [
[
100,
10
]
],
"b" : [
[
4,
4
]
]
},
"server" :""
}
输出的说明是扫描索引,这就是nscanned为588(扫描的索引条目和文档的数量)的原因,nscannedObjects处的数字是扫描文档的数量。所以基于索引mongo只读取符合条件的文档(索引部分覆盖左右)。正如您所看到的,scanAndOrder为false,因此没有排序阶段。 (这意味着如果索引在内存中会很快)
与其他人链接的文章一起:http://blog.mongolab.com/wp-content/uploads/2012/06/IndexVisitation-4.png你必须首先在索引和查询键之后放置排序键,如果它们有子集匹配,你必须以相同的顺序包含子集因为它们在排序标准中(而对于查询部分无关紧要)。
答案 3 :(得分:0)
我认为最好更改find中字段的顺序。
db.mycollection.find({b:4, a:{$gt:10,$lt:100}}).sort({c:-1, a:-1})
然后添加索引
{b:1,a:-1,c:-1}
答案 4 :(得分:0)
我尝试了两个不同的索引,
索引的顺序为db.mycollection.ensureIndex({a:1,b:1,c:-1})
和解释计划如下所示
{
"cursor" : "BtreeCursor a_1_b_1_c_-1",
"nscanned" : 9542,
"nscannedObjects" : 1,
"n" : 1,
"scanAndOrder" : true,
"millis" : 36,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : false,
"indexOnly" : false,
"indexBounds" : {
"a" : [
[
3,
10000
]
],
"b" : [
[
4,
4
]
],
"c" : [
[
{
"$maxElement" : 1
},
{
"$minElement" : 1
}
]
]
}
}
和db.mycollection.ensureIndex({b:1,c:-1,a:-1})
> db.mycollection.find({a:{$gt:3,$lt:10000},b:4}).sort({c:-1, a:-1}).explain()
{
"cursor" : "BtreeCursor b_1_c_-1_a_-1",
"nscanned" : 1,
"nscannedObjects" : 1,
"n" : 1,
"millis" : 8,
"nYields" : 0,
"nChunkSkips" : 0,
"isMultiKey" : false,
"indexOnly" : false,
"indexBounds" : {
"b" : [
[
4,
4
]
],
"c" : [
[
{
"$maxElement" : 1
},
{
"$minElement" : 1
}
]
],
"a" : [
[
10000,
3
]
]
}
}
>
我相信,既然您在一系列值上查询'a'而在特定值上查询'b',我猜第二种选择更合适。 nscanned对象从9542更改为1