MongoDB使用错误的索引

时间:2012-09-24 16:54:27

标签: mongodb indexing sharding optimization

我无法理解这个问题 - 我有一个分片集群,其中一个分片(分片2)似乎使用了错误的索引。我通过分片键查询,这是网站ID和第一个请求时间{ site.id: 1, frt: 1 }。我还有一个关于网站ID和上次请求时间的索引。

在这个查询中,我也试图通过我在文档中设置的几个布尔值来限制返回的文档。

阅读关于Mongo的查询优化器如何工作的文档,对于我查看返回的Explains来说,这似乎特别奇怪。文档:Query Optimizer

我还在Shard 1中包含了一个解释,其中查询按预期返回。最后,如果我使用没有存储在Shard 2上的块的站点ID,它会使用正确的索引,尽管它没有任何内容可以扫描或返回。为完整性添加了最终解释。

为什么会发生这种情况和/或这是一个错误?

基本查询(错误索引):

shard2:PRIMARY> db.visit.find({ "site.id": 128, "frt": { $gte: new Date(2012, 8, 24 ) }, "ue": false, "bot": false }).explain()
{
    "cursor" : "BtreeCursor site.id_1_lrt_-1",
    "isMultiKey" : false,
    "n" : 198,
    "nscannedObjects" : 61204,
    "nscanned" : 61204,
    "nscannedObjectsAllPlans" : 61537,
    "nscannedAllPlans" : 61537,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 122,
    "nChunkSkips" : 0,
    "millis" : 727,
    "indexBounds" : {
        "site.id" : [
            [
                128,
                128
            ]
        ],
        "lrt" : [
            [
                {
                    "$maxElement" : 1
                },
                {
                    "$minElement" : 1
                }
            ]
        ]
    },
    "server" : "ip-10-4-211-107:2200"
}

提供提示:

shard2:PRIMARY> db.visit.find({ "site.id": 128, "frt": { $gte: new Date(2012, 8, 24 ) }, "ue": false, "bot": false }).hint("site.id_1_frt_1").explain()
{
    "cursor" : "BtreeCursor site.id_1_frt_1",
    "isMultiKey" : false,
    "n" : 198,
    "nscannedObjects" : 486,
    "nscanned" : 486,
    "nscannedObjectsAllPlans" : 486,
    "nscannedAllPlans" : 486,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 5,
    "indexBounds" : {
        "site.id" : [
            [
                128,
                128
            ]
        ],
        "frt" : [
            [
                ISODate("2012-09-24T07:00:00Z"),
                ISODate("292278995-01--2147483647T07:12:56.808Z")
            ]
        ]
    },
    "server" : "ip-10-4-211-107:2200"
}

相同的查询没有其他布尔限制(使用正确的索引):

shard2:PRIMARY> db.visit.find({ "site.id": 128, "frt": { $gte: new Date(2012, 8, 24 ) } }).explain()
{
    "cursor" : "BtreeCursor site.id_1_frt_1",
    "isMultiKey" : false,
    "n" : 486,
    "nscannedObjects" : 486,
    "nscanned" : 486,
    "nscannedObjectsAllPlans" : 486,
    "nscannedAllPlans" : 486,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 1,
    "indexBounds" : {
        "site.id" : [
            [
                128,
                128
            ]
        ],
        "frt" : [
            [
                ISODate("2012-09-24T07:00:00Z"),
                ISODate("292278995-01--2147483647T07:12:56.808Z")
            ]
        ]
    },
    "server" : "ip-10-4-211-107:2200"
}

在Shard 1上,原始查询使用预期索引:

shard1:PRIMARY> db.visit.find({ "site.id": 253, "frt": { $gte: new Date(2012, 8, 24 ) }, "ue": false, "bot": false }).explain()
{
    "cursor" : "BtreeCursor site.id_1_frt_1",
    "isMultiKey" : false,
    "n" : 15615,
    "nscannedObjects" : 15950,
    "nscanned" : 15950,
    "nscannedObjectsAllPlans" : 16152,
    "nscannedAllPlans" : 16152,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 125,
    "nChunkSkips" : 0,
    "millis" : 237,
    "indexBounds" : {
        "site.id" : [
            [
                253,
                253
            ]
        ],
        "frt" : [
            [
                ISODate("2012-09-24T07:00:00Z"),
                ISODate("292278995-01--2147483647T07:12:56.808Z")
            ]
        ]
    },
    "server" : "ip-10-6-50-253:2100"
}

在此处没有任何块的网站的分片2上查询(使用正确的索引):

shard2:PRIMARY> db.visit.find({ "site.id": 253, "frt": { $gte: new Date(2012, 8, 24 ), "ue": false, "bot": false } }).explain()
{
    "cursor" : "BtreeCursor site.id_1_frt_1",
    "isMultiKey" : false,
    "n" : 0,
    "nscannedObjects" : 0,
    "nscanned" : 0,
    "nscannedObjectsAllPlans" : 0,
    "nscannedAllPlans" : 0,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 0,
    "nChunkSkips" : 0,
    "millis" : 0,
    "indexBounds" : {
        "site.id" : [
            [
                253,
                253
            ]
        ],
        "frt" : [
            [
                ISODate("2012-09-24T07:00:00Z"),
                ISODate("292278995-01--2147483647T07:12:56.808Z")
            ]
        ]
    },
    "server" : "ip-10-4-211-107:2200"
}

1 个答案:

答案 0 :(得分:3)

您链接的文档中的一些内容可能会解释此行为,首先:

  

在1,000次操作之后以及之后重复测试查询   发生某些集合操作(​​例如添加索引)。

因此,如果您没有足够的查询量来进行评估,那么它将坚持其首选。

第二

  

要解决此问题,在测试新计划时,MongoDB会执行多个查询   并行计划。一旦完成,它就会终止另一个   执行,系统已经知道哪个计划是好的。

如果另一个索引已经在内存中,比如因为它正在被另一个查询使用,或者正在发生其他事情,那么会降低首选索引上的查询执行速度(或者它非常接近,偶尔会交换它们的术语速度),那么你将再次返回“坏”索引。

优化器已在2.2中进行了调整和改进,因此如果您仍然遇到问题(并且在2.0或更低版本),可能值得一看。或者,正如您在测试中所做的那样,如果您知道要使用的最佳索引,只需删除所有疑问并使用提示来指定它。