为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为我选择正确(最快)的索引,特别是对于重复使用的查询

您的查询中有两个问题:

  • 从不对未编制索引的参数进行排序。如果
    .find()
    中的文档量很大,则会出现以下错误:“sort()没有索引的数据太多”,大小取决于您使用的mongo版本。这意味着您必须在
    A
    C
    上有索引才能使查询正常工作
  • 现在来看更大的问题。您正在对param
    a
    执行范围查询(
    $lt
    $gt
    ),该查询无法与Mongo一起使用。MongoDB一次只使用一个索引,您在同一个参数上使用两个索引。在您的代码中有几种解决方案:

  • 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
            ]
        ]
    }
}
>