【问题标题】:Simple MongoDB query very slow although index is set尽管设置了索引,但简单的 MongoDB 查询非常慢
【发布时间】:2012-04-01 20:38:34
【问题描述】:

我有一个 MongoDB 集合,其中包含大约 1 亿个文档。

文件基本上是这样的:

_id             : ObjectId("asd1234567890")
_reference_1_id : ObjectId("fgh4567890123")
_reference_2_id : ObjectId("jkl7890123456")
name            : "Test1"
id              : "4815162342"
created_time    : Date( 1331882436000 )
_contexts       : ["context1", "context2"]
...

设置了一些索引,这里是db.mycoll.getIndexes()的输出;

[
{
    "v" : 1,
    "key" : {
        "_id" : 1
    },
    "ns" : "mydb.mycoll",
    "name" : "_id_"
},
{
    "v" : 1,
    "key" : {
        "_reference_1_id" : 1,
        "_reference_2_id" : 1,
        "id" : 1
    },
    "unique" : true,
    "ns" : "mydb.mycoll",
    "name" : "_reference_1_id_1__reference_2_id_1_id_1"
},
{
    "v" : 1,
    "key" : {
        "_reference_1_id" : 1,
        "_reference_2_id" : 1,
        "_contexts" : 1,
        "created_time" : 1
    },
    "ns" : "mydb.mycoll",
    "name" : "_reference_1_id_1__reference_2_id_1__contexts_1_created_time_1"
}
]

当我执行类似的查询时

db.mycoll.find({"_reference_2_id" : ObjectId("jkl7890123456")})

无论是否有结果,它都需要一个多小时 (!) 才能完成。 有什么想法吗?

更新: 这是

的输出
db.mycoll.find({"_reference_2_id" : ObjectId("jkl7890123456")}).explain();

看起来像:

{
"cursor" : "BasicCursor",
"nscanned" : 99209163,
"nscannedObjects" : 99209163,
"n" : 5007,
"millis" : 5705175,
"nYields" : 17389,
"nChunkSkips" : 0,
"isMultiKey" : false,
"indexOnly" : false,
"indexBounds" : {

}
}

【问题讨论】:

    标签: performance mongodb indexing


    【解决方案1】:

    您没有任何 mongo 会自动使用的索引,因此它正在执行全表扫描。

    the docs中所述

    如果查询中不存在[索引的]第一个键,则只有在明确提示时才会使用索引。

    为什么

    如果您在 a,b 上有一个索引 - 并且您仅通过 a 搜索 - 将自动使用一个索引。这是因为它是索引的开始(这样做很快),数据库可以忽略索引值的其余部分。

    单独通过b 搜索时,a、b 上的索引效率低下,因为它无法使用“以该固定字符串开头”的索引搜索。

    所以,要么:

    • 在查询中包含 _reference_1_id(可能不相关)
    • 或在 _reference_2_id 上添加索引(如果您经常按字段查询)
    • 或使用提示

    提示

    可能是您目前成本最低的选择。

    添加查询提示以强制使用您的 _reference_1_id_1__reference_2_id_1_id_1 索引。这可能比全表扫描快很多,但仍然比以您在查询中使用的字段开头的索引慢很多。

    db.mycoll
        .find({"_reference_2_id" : ObjectId("jkl7890123456")})
        .hint("_reference_1_id_1__reference_2_id_1_id_1");
    

    【讨论】:

    • 完美,感谢您的详细回答一百万!我实际上可以包含 _reference_1_id,所以我什至不需要提示索引。
    【解决方案2】:

    我会尝试在_reference_2_id 上设置一个非唯一索引,因为目前,我怀疑您将执行相当于全表扫描的操作,即使索引包含_reference_2_id,它们也不会被使用(见here)。

    【讨论】:

    • 查看 explain() 的输出(请参阅更新的问题),您说得对,我正在执行全表扫描而不使用索引。但为什么会这样呢?我在您 linked 的 MongoDB 文档中看不到任何相关信息。
    【解决方案3】:

    嘿, 我在同等数量的数据上解决了同样的问题。在文档中,写到带有索引的查询必须适合 ram。我认为不是这样的,查询一定是做了大量的磁盘访问才能先检索索引再获取值。在您的情况下,直接读取集合会更快。

    电动汽车。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-29
      • 1970-01-01
      • 2018-07-09
      • 2013-02-21
      • 1970-01-01
      相关资源
      最近更新 更多