Mongodb 多键索引上的慢速范围查询

Mongodb 多键索引上的慢速范围查询,mongodb,indexing,mongodb-indexes,Mongodb,Indexing,Mongodb Indexes,我有一个名为post的MongoDB集合,包含3500万个对象。集合有两个二级索引,定义如下 > db.post.getIndexKeys() [ { "_id" : 1 }, { "namespace" : 1, "domain" : 1, "post_id" : 1 }, { "namespace" : 1, "post_time" : 1,

我有一个名为
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
    }
]
我希望下面的查询只通过
名称空间
发布时间
进行过滤,在合理的时间内运行,而不扫描所有对象

>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检索结果至少需要10分钟,奇怪的是,它能够扫描7000万个对象,根据
explain
功能完成这项工作

> 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\u time
filter不使用索引

你能告诉我我可能遗漏了什么吗


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

找到了这个问题的答案:

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


不幸的是,这并不是全部。为了解决这个问题,我也创建了非多键索引,但MongoDB坚持使用坏的索引。这让我觉得问题出在别处。最后,我不得不删除多键索引,创建一个没有
标记的索引。现在一切正常。

名称空间的基数很低吗?目前,只有一个不同的
名称空间
值,这就是我正在使用的值。这就是为什么MongoDB必须首先限制第一个字段,所以它获取所有
我的名称空间
,然后获取该日期之间的所有文档,等等,尝试重新排序索引,使post_时间在第一位索引树的
my_namespace
分支下不也包含
post_时间
值吗?为什么要在缩小范围之前开始扫描?即使btree包含两个字段,复合索引在MongoDB中也会以某种方式工作,这意味着它将扫描所有名称空间以查找该值,然后缩小日期范围。嗯,我试图找到一个能很好地解释它的文档页面,但是在谷歌搜索的索引内部没有真正好的文档页面,尽管这篇文章可能会有所帮助:当我从来都不知道关于
$gt
$lt
和miltikeys,很好的发现!使用cursor.hint也可以解决让mongodb使用其他索引()的问题