6

我正在运行很多这种类型的查询:

db.mycollection.find({a:{$gt:10,$lt:100}, b:4}).sort({c:-1, a:-1})

我应该使用哪种索引来加快速度?我想我需要同时拥有{a:1, b:1}and {c:-1, a:-1},对吗?或者这些索引会以某种方式相互干扰而没有性能提升?

编辑:对我来说实际的问题是我在一个循环中运行了许多查询,其中一些在小范围内,其他在大范围内。如果我把 index on {a:1, b:1},它会很快选择小块,但是当涉及到大范围时,我会看到一个错误“sort() 没有索引的数据太多”。如果,否则,我把 index on {c:-1, a:-1},没有错误,但较小的块(还有更多的块)处理得慢得多。那么,如何在较小的范围内保持选择的速度,但在大量数据上不会出错呢?

如果重要的话,我会通过 Python 的 pymongo 运行查询。

4

5 回答 5

4

免责声明:对于 MongoDB v2.4

使用提示是一个很好的解决方案,因为它会强制查询使用您选择的索引,因此您可以使用不同的索引优化查询,直到您满意为止。缺点是您要为每个请求设置自己的索引。我更喜欢在整个集合上设置索引,让 Mongo为我选择正确(最快)的索引,尤其是对于重复使用的查询。

您的查询中有两个问题:

  • 永远不要对未编入索引的参数进行排序。如果您的文档数量非常大,您将收到此错误:“sort() 没有索引的数据过多”.find(),大小取决于您使用的 mongo 版本。这意味着您必须有索引A才能C让您的查询正常工作。
  • 现在解决更大的问题。您正在执行范围查询($lt$gt在 param 上A),这不能与 Mongo 一起使用。MongoDB 一次只使用一个索引,您在同一个参数上使用两个索引。有几种解决方案可以在您的代码中处理它:

    1. r = range( 11,100 )
      db.mycollection.find({a:{$in: r }, b:4}).sort({c:-1, a:-1})

    2. 仅使用$lt$gt在您的查询中使用,
      db.mycollection.find({ a: { $lt:100 }, b:4}).sort({c:-1, a:-1})
      获取结果并在您的 python 代码中过滤它们。 此解决方案将返回更多数据,因此,如果您有数百万个结果少于 then A=11,请不要使用它!如果您选择此选项,
      请确保使用复合键和。 AB

在查询中使用时要注意$or,因为 $or 的优化效率低于$in使用索引。

于 2013-10-07T14:04:15.270 回答
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它们在索引方面遇到同样的问题,这篇文章应该提供答案:http ://blog.mongolab.com/2012/06/cardinal-ins/真的没有任何理由重复它内容。

于 2013-09-25T07:43:20.253 回答
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 为假,因此没有排序阶段。(这意味着索引是否在内存中会很快)

连同其他人链接的文章:http: //blog.mongolab.com/wp-content/uploads/2012/06/IndexVisitation-4.png您必须首先将排序键放在索引中,然后将查询键放在后面,如果它们有子集匹配,则必须以与排序条件中相同的顺序包含子集(尽管对于查询部分无关紧要)。

于 2013-10-01T15:50:36.167 回答
0

我认为更改查找中字段的顺序会更好。

db.mycollection.find({b:4, a:{$gt:10,$lt:100}}).sort({c:-1, a:-1})

然后你添加一个索引

{b:1,a:-1,c:-1}
于 2013-10-03T09:33:00.767 回答
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

于 2013-10-03T16:03:04.123 回答