MongoDB:即使有索引,查询速度仍然缓慢

7
我有一个网页,使用MongoDB存储和检索各种度量值。突然间,我的网页变得非常缓慢,无法使用。原来,我的数据库是罪魁祸首。
我搜索了很久也没有找到解决办法,我很抱歉,因为我对MongoDB很新,并且现在感到非常苦恼。
我使用的MongoDB版本是2.4.6,在具有20GB RAM的VM机器上运行Ubuntu服务器12.04。没有设置备份或分片。
首先,我将我的分析级别设置为2,并且它显示了最慢的查询:
db.system.profile.find().sort({"millis":-1}).limit(1).pretty()
{
        "op" : "query",
        "ns" : "station.measurement",
        "query" : {
                "$query" : {
                        "e" : {
                                "$gte" : 0
                        },
                        "id" : "180"
                },
                "$orderby" : {
                        "t" : -1
                }
        },
        "ntoreturn" : 1,
        "ntoskip" : 0,
        "nscanned" : 3295221,
        "keyUpdates" : 0,
        "numYield" : 6,
        "lockStats" : {
                "timeLockedMicros" : {
                        "r" : NumberLong(12184722),
                        "w" : NumberLong(0)
                },
                "timeAcquiringMicros" : {
                        "r" : NumberLong(5636351),
                        "w" : NumberLong(5)
                }
        },
        "nreturned" : 0,
        "responseLength" : 20,
        "millis" : 6549,
        "ts" : ISODate("2015-03-16T08:57:07.772Z"),
        "client" : "127.0.0.1",
        "allUsers" : [ ],
        "user" : ""
}

我使用了.explain()运行了特定查询,看起来它正在使用索引,但是时间太长了。我还在我的另一个性能极差的服务器上运行了同样的查询,结果在一秒钟内轻松输出。

> db.measurement.find({"id":"180", "e":{$gte:0}}).sort({"t":-1}).explain()
{
        "cursor" : "BtreeCursor id_1_t_-1_e_1",
        "isMultiKey" : false,
        "n" : 0,
        "nscannedObjects" : 0,
        "nscanned" : 660385,
        "nscannedObjectsAllPlans" : 1981098,
        "nscannedAllPlans" : 3301849,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 7,
        "nChunkSkips" : 0,
        "millis" : 7243,
        "indexBounds" : {
                "id" : [
                        [
                                "180",
                                "180"
                        ]
                ],
                "t" : [
                        [
                                {
                                        "$maxElement" : 1
                                },
                                {
                                        "$minElement" : 1
                                }
                        ]
                ],
                "e" : [
                        [
                                0,
                                1.7976931348623157e+308
                        ]
                ]
        },
        "server" : "station:27017"
}

接下来,我看了一下 测量 集合的索引,感觉还不错:

> db.measurement.getIndexes()
[
        {
                "v" : 1,
                "key" : {
                        "_id" : 1
                },
                "ns" : "station.measurement",
                "name" : "_id_"
        },
        {
                "v" : 1,
                "key" : {
                        "t" : 1
                },
                "ns" : "station.measurement",
                "name" : "t_1"
        },
        {
                "v" : 1,
                "key" : {
                        "id" : 1,
                        "d" : 1,
                        "_id" : -1
                },
                "ns" : "station.measurement",
                "name" : "id_1_d_1__id_-1"
        },
        {
                "v" : 1,
                "key" : {
                        "id" : 1,
                        "t" : -1,
                        "e" : 1
                },
                "ns" : "station.measurement",
                "name" : "id_1_t_-1_e_1"
        },
        {
                "v" : 1,
                "key" : {
                        "id" : 1,
                        "t" : -1,
                        "e" : -1
                },
                "ns" : "station.measurement",
                "name" : "id_1_t_-1_e_-1"
        }
]

这里是我收藏的其他信息:

> db.measurement.stats()
{
        "ns" : "station.measurement",
        "count" : 157835456,
        "size" : 22377799512,
        "avgObjSize" : 141.77929395027692,
        "storageSize" : 26476834672,
        "numExtents" : 33,
        "nindexes" : 5,
        "lastExtentSize" : 2146426864,
        "paddingFactor" : 1.0000000000028617,
        "systemFlags" : 0,
        "userFlags" : 0,
        "totalIndexSize" : 30996614096,
        "indexSizes" : {
                "_id_" : 6104250656,
                "t_1" : 3971369360,
                "id_1_d_1__id_-1" : 8397896640,
                "id_1_t_-1_e_1" : 6261548720,
                "id_1_t_-1_e_-1" : 6261548720
        },
        "ok" : 1
}

我尝试添加新的索引,修复整个数据库,重新建立索引。我做错了什么?由于我已经没有更多的想法了,所以非常感谢任何帮助。
更新1: 如Neil Lunn所建议的那样,我添加了两个索引,一些查询速度快了很多。
{
                "v" : 1,
                "key" : {
                        "id" : 1,
                        "e" : 1,
                        "t" : -1
                },
                "ns" : "station.measurement",
                "name" : "id_1_e_1_t_-1",
                "background" : true
        },
        {
                "v" : 1,
                "key" : {
                        "id" : 1,
                        "e" : -1,
                        "t" : -1
                },
                "ns" : "station.measurement",
                "name" : "id_1_e_-1_t_-1",
                "background" : true
        }

我得到的结果很有趣(不确定它们是否相关)

下面两个查询仅通过"id"不同。请注意,每个查询使用不同的索引,为什么?我应该删除旧的吗?

> db.measurement.find({"id":"119", "e":{$gte:0}}).sort({"t":-1}).explain()
{
        "cursor" : "BtreeCursor id_1_t_-1_e_1",
        "isMultiKey" : false,
        "n" : 840747,
        "nscannedObjects" : 840747,
        "nscanned" : 1047044,
        "nscannedObjectsAllPlans" : 1056722,
        "nscannedAllPlans" : 1311344,
        "scanAndOrder" : false,
        "indexOnly" : false,
        "nYields" : 4,
        "nChunkSkips" : 0,
        "millis" : 3730,
        "indexBounds" : {
                "id" : [
                        [
                                "119",
                                "119"
                        ]
                ],
                "t" : [
                        [
                                {
                                        "$maxElement" : 1
                                },
                                {
                                        "$minElement" : 1
                                }
                        ]
                ],
                "e" : [
                        [
                                0,
                                1.7976931348623157e+308
                        ]
                ]
        },
        "server" : "station:27017"
}

> db.measurement.find({"id":"180", "e":{$gte:0}}).sort({"t":-1}).explain()
{
        "cursor" : "BtreeCursor id_1_e_1_t_-1",
        "isMultiKey" : false,
        "n" : 0,
        "nscannedObjects" : 0,
        "nscanned" : 0,
        "nscannedObjectsAllPlans" : 0,
        "nscannedAllPlans" : 45,
        "scanAndOrder" : true,
        "indexOnly" : false,
        "nYields" : 0,
        "nChunkSkips" : 0,
        "millis" : 0,
        "indexBounds" : {
                "id" : [
                        [
                                "180",
                                "180"
                        ]
                ],
                "e" : [
                        [
                                0,
                                1.7976931348623157e+308
                        ]
                ],
                "t" : [
                        [
                                {
                                        "$maxElement" : 1
                                },
                                {
                                        "$minElement" : 1
                                }
                        ]
                ]
        },
        "server" : "station:27017"
}

问题可能出在其他地方吗?是什么导致了突然的“迟缓”?我有几个收藏夹,查询也突然变慢。

哦,还有一件事。在我拥有的另一个服务器上,索引与此处添加新索引之前相同。是的,收藏夹稍小,但速度快了好几倍。


2
问题表述得很好。看起来你正在扫描比“e”更多的“t”条目。作为一个实验,尝试改变索引和查询顺序,将有序的重点放在“t”之前的“e”上。更改文档建议这不应该改变结果,但如果有差异,你的结果会很有趣。 - Neil Lunn
有趣的建议,谢谢!我会添加新的索引(id_1_e_1_t_-1和id_1_e_-1_t_-1),并尽快让您知道结果,因为建立索引需要一些时间。 - Denis
嘿,我成功地构建了那些索引,并使用一些查询进行了测试。我已经更新了我的问题并附上了结果 :) - Denis
结果似乎看到了积极的改善。你明白为什么吗?还是我们需要进一步探讨? - Neil Lunn
我不明白为什么在查询中选择了另一个索引,而只有“id”值不同。我肯定在这里漏掉了什么...请告诉我更多。我非常非常感谢你的帮助!难道是我某种方式损坏了我的数据库吗? - Denis
你是否有ID为180的840747个文档? - Kim D.
1个回答

2

这里的重点是在索引和查询排序选择中。

如果您查看早期的.explain()输出,您会发现表达式中的"t"元素上有一个"min/max"范围。通过将其"移动到评估的末尾",您允许其他过滤元素更重要地影响整个表达式,确定较少可能匹配"e"的主要因素,然后在基本上"所有内容"中扫描"t"。

这有点像DBA,但在NoSQL世界中,我确信这成为了程序员的问题。

基本上,您需要沿着所选键构建"最短匹配路径",以便获得最有效的扫描。这就是为什么修改后的结果执行速度更快的原因。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接