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使用其他索引()的问题