为MongoDB编制索引,以便在不同字段上使用sort()更快地查找()
我正在运行大量此类查询:为MongoDB编制索引,以便在不同字段上使用sort()更快地查找(),mongodb,indexing,Mongodb,Indexing,我正在运行大量此类查询: 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}上,它会很快地选择小的块,但当涉及到大范围时,我会看
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运行查询。如果您阅读了文档,您会发现在这里使用两个索引是没有用的,因为MongoDB在实现之前每个查询只使用一个索引(除非是
$或)
不仅如此,而且在使用复合排序时,索引中的顺序必须与正确使用索引的排序顺序相匹配,例如:
或者这些索引会以某种方式相互干扰,而没有性能增益
如果执行了交叉,则不会执行,{a:1,b:1}
与排序不匹配,{c:-1,a:-1}
对于回答find()
加上a
不是该复合词的前缀是次优的
因此,最佳索引的迭代立即将是:
{a:-1,b:1,c:-1}
但这并不是全部。由于$gt
和$lt
实际上是范围,就像$in
一样,它们在索引方面遇到了同样的问题,因此本文应该给出答案:没有任何理由重复它的内容。如果您定义一个索引{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});}
输出的解释是索引被扫描,这就是为什么是588(扫描的索引条目和文档的数量),at是扫描的文档的数量。因此,基于索引,mongo只读取符合条件的文档(索引部分覆盖左右)。如您所见,Scanander为false,因此没有排序阶段。(这意味着索引是否在内存中,这将非常快)
与文章what others linked(其他链接的内容)一起:您必须首先在索引中放置排序键,然后再放置查询键,如果它们有子集匹配,则必须按照与排序条件相同的顺序包含子集(而这对于查询部分并不重要) 我认为最好更改find中字段的顺序
db.mycollection.find({b:4, a:{$gt:10,$lt:100}}).sort({c:-1, a:-1})
然后添加一个索引
{b:1,a:-1,c:-1}
我尝试了两种不同的索引
索引顺序为
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})的索引
我相信,由于您查询的是一系列值的“a”,而查询的是特定值的“b”,所以我认为第二个选项更合适。N扫描对象从9542更改为1
免责声明:适用于MongoDB v2.4
使用hint是一个很好的解决方案,因为它将强制查询使用您选择的索引,因此您可以使用不同的索引优化查询,直到您满意为止。缺点是,您正在为每个请求设置自己的索引。
我更喜欢在整个集合上设置索引,让Mongo为我选择正确(最快)的索引,特别是对于重复使用的查询
您的查询中有两个问题:
- 从不对未编制索引的参数进行排序。如果
中的文档量很大,则会出现以下错误:“sort()没有索引的数据太多”,大小取决于您使用的mongo版本。这意味着您必须在.find()
和A
上有索引才能使查询正常工作C
- 现在来看更大的问题。您正在对param
执行范围查询(a
和$lt
),该查询无法与Mongo一起使用。MongoDB一次只使用一个索引,您在同一个参数上使用两个索引。在您的代码中有几种解决方案:$gt
r=范围(11100)
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
如果选择此选项,请确保将a与
和a
一起使用B
在查询中使用
$或时请注意,因为在使用索引时,
中的$or大于$。
应该是索引中的最后一个。黎族
{
"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.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
]
]
}
}
>