对多键索引进行慢速查询

时间:2013-05-09 11:17:02

标签: mongodb indexing mongodb-indexes

我有一个名为post的MongoDB集合,其中包含 3500万个对象。该集合有两个二级索引,定义如下。

> db.post.getIndexKeys()
[
    {
        "_id" : 1
    },
    {
        "namespace" : 1,
        "domain" : 1,
        "post_id" : 1
    },
    {
        "namespace" : 1,
        "post_time" : 1,
        "tags" : 1  // this is an array field
    }
]

我希望以下查询(仅按namespacepost_time过滤)在合理的时间内运行,而不扫描所有对象。

>db.post.find({post_time: {"$gte" : ISODate("2013-04-09T00:00:00Z"), "$lt" : ISODate("2013-04-09T01:00:00Z")}, namespace: "my_namespace"}).count()
7408

然而,MongoDB需要至少十分钟来检索结果,奇怪的是,它设法根据explain函数扫描 7000万对象来完成工作。

> db.post.find({post_time: {"$gte" : ISODate("2013-04-09T00:00:00Z"), "$lt" : ISODate("2013-04-09T01:00:00Z")}, namespace: "my_namespace"}).explain()
{
    "cursor" : "BtreeCursor namespace_1_post_time_1_tags_1",
    "isMultiKey" : true,
    "n" : 7408,
    "nscannedObjects" : 69999186,
    "nscanned" : 69999186,
    "nscannedObjectsAllPlans" : 69999186,
    "nscannedAllPlans" : 69999186,
    "scanAndOrder" : false,
    "indexOnly" : false,
    "nYields" : 378967,
    "nChunkSkips" : 0,
    "millis" : 290048,
    "indexBounds" : {
        "namespace" : [
            [
                "my_namespace",
                "my_namespace"
            ]
        ],
        "post_time" : [
            [
                ISODate("2013-04-09T00:00:00Z"),
                ISODate("292278995-01--2147483647T07:12:56.808Z")
            ]
        ],
        "tags" : [
            [
                {
                    "$minElement" : 1
                },
                {
                    "$maxElement" : 1
                }
            ]
        ]
    },
    "server" : "localhost:27017"
}

对象数量和扫描次数之间的差异必须由标签阵列的长度(均等于2)引起。不过,我不明白为什么post_time过滤器没有使用索引。

你能告诉我可能缺少什么吗?

(我正在研究一台24核和96 GB RAM的下降机。我使用的是MongoDB 2.2.3。)

1 个答案:

答案 0 :(得分:3)

在这个问题中找到我的答案:Order of $lt and $gt in MongoDB range query

我的索引是一个多键索引(在tags上),我正在运行一个范围查询(在post_time上)。 Apparently,在这种情况下,MongoDB不能将范围的两边都用作过滤器,所以它只选择$gte子句,这是第一个。由于我的下限恰好是最低post_time值,MongoDB开始扫描所有对象。

不幸的是,这不是全部。为了解决这个问题,我也创建了非多键索引,但MongoDB坚持使用坏的索引。这让我觉得问题出在其他地方。最后,我不得不删除多键索引并创建一个没有tags字段的索引。一切都很好。