【问题标题】:MongoDB uses wrong indexMongoDB 使用错误的索引
【发布时间】:2012-09-24 16:54:27
【问题描述】:

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

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

阅读有关 Mongo 查询优化器如何工作的文档,这对我来说似乎特别奇怪,因为我查看了返回的解释。文档在这里:Query Optimizer

我还包含了来自 Shard 1 的解释,其中查询按预期返回。最后,如果我使用的站点 ID 没有存储在 Shard 2 上的块,它会使用正确的索引,尽管它没有任何东西可以扫描或返回。为了完整起见,在末尾添加了对此的解释。

任何想法为什么会发生这种情况和/或如果这是一个错误?

基本查询(错误索引):

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"
}

在分片 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"
}

在 Shard 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"
}

【问题讨论】:

  • tldr?添加到查询时索引会丢失?
  • 你的分片键是什么?您提到您的查询db.visit.find({ "site.id": 128, "frt": { $gte: new Date(2012, 8, 24 ) }, "ue": false, "bot": false }).explain() 在“Shard2”上命中了错误的索引,但您没有包括在“Shard1”上运行此查询的输出。所有针对“Shard1”的查询都是针对"site.id" 128。您能否针对“Shard1”运行“128”查询。
  • 上面提到过,分片键是 { site.id: 1, frt: 1 },分片在我尝试使用的同一索引上。在 Shard 1 上运行对 "site.id" 128 的查询仍然使用正确的索引,但由于块的拆分方式,该站点没有数据。我选择"site.id": 253 作为上面 Shard 1 的示例,因为我知道那里有数据。
  • 认为您可能混淆了上面的示例,“所有针对“Shard1”的查询都是针对“site.id”128。” - 实际上,以上查询都没有查询 Shard 1 上的站点 128...

标签: mongodb indexing sharding optimization


【解决方案1】:

您链接的文档中的几件事可能解释了这种行为,首先:

在 1,000 次操作后重复查询测试 对集合进行某些操作(例如添加索引)。

因此,如果您没有足够的查询量来评估它,它会坚持首选。

第二:

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

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

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

【讨论】:

  • 感谢您的帖子。我应该包含在我的帖子中,这是在 2.2 上。引用的第二段是我发现的奇怪,如果确实并行运行查询计划以确定“最佳”,这似乎很奇怪,因为两个查询计划之间的时间差非常大。我还尝试设置一个循环并运行此查询 1000 多次,看看我是否可以欺骗优化器重新评估查询计划。没有骰子。我首先注意到使用聚合查询时的性能问题,其中没有选项可以在 $match 运算符上使用提示。
  • 听起来这可能是一个错误 - 您是否尝试过(暂时)删除坏索引,运行查询,然后重新添加它作为一种潜在的解决方法(尽管是一个坏的)
  • 是的——我想我会开一张票。我删除了{site.id: 1, lrt: -1} 索引并运行了查询,它使用了正确的索引。重建该索引并再次运行查询,它再次使用不正确的索引。
猜你喜欢
  • 2014-04-25
  • 2014-10-16
  • 2013-06-09
  • 2014-04-16
  • 2017-01-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-04-06
相关资源
最近更新 更多