Mongo范围查询在数组内的对象上运行缓慢

时间:2016-09-27 11:27:34

标签: mongodb mongodb-query

我有一个收藏品 订单

{
    "_id": "abcd",
    "last_modified": ISODate("2016-01-01T00:00:00Z"),
    "suborders": [
        {
            "suborder_id": "1",
            "last_modified: ISODate("2016-01-02T00: 00: 00Z")
        },  {
            "suborder_id":"2",
            "last_modified: ISODate("2016-01-03T00:00:00Z")
        }
    ]
}

我在这个集合上有两个索引: { “LAST_MODIFIED”:1} {“suborders.last_modified”:1}

当我在last_modified上使用范围查询时,正确使用索引,并立即返回结果。例如查询:db.orders.find({"last_modified":{$gt:ISODate("2016-09-15"), $lt:ISODate("2016-09-16")}});

但是,当我查询suborders.last_modified时,查询执行时间太长。 eq查询:db.orders.find({"suborders.last_modified":{$gt:ISODate("2016-09-15"), $lt:ISODate("2016-09-16")}});

请帮忙调试一下。

1 个答案:

答案 0 :(得分:1)

简短的回答是使用minmax来正确设置索引边界。关于如何进行调试,请继续阅读。

查询性能问题的一个好地方是在查询结束时附加.explain()。我制作了一个script来生成像你这样的文档并执行你提供的查询。

我使用了mongo 3.2.9,并且两个查询都使用此设置创建的索引。但是,第二个查询返回了更多文档(大约占集合中所有文档的6%)。我怀疑那不是你的意图。

要了解发生了什么,可以考虑一下mongo shell中的一个小例子:

> db.arrayFun.insert({
    orders: [
        { last_modified: ISODate("2015-01-01T00:00:00Z") },
        { last_modified: ISODate("2016-01-01T00:00:00Z") }
    ]
})
WriteResult({ "nInserted" : 1 })

然后在2015年5月到7月之间进行查询:

> db.arrayFun.find({"orders.last_modified": {
    $gt: ISODate("2015-05-01T00:00:00Z"),  
    $lt: ISODate("2015-07-01T00:00:00Z")
}}, {_id: 0})
{ "orders" : [ { "last_modified" : ISODate("2015-01-01T00:00:00Z") }, { "last_modified" : ISODate("2016-01-01T00:00:00Z") } ] }

虽然数组中的任何对象在5月到7月之间都没有last_modified,但它找到了该文档。这是因为它在数组中查找一个对象,其中last_modified大于五月,一个对象的last_modified小于七月。这些查询cannot intersect multikey index bounds,在您的情况下发生。您可以在indexBounds输出的explain("allPlansExecution")字段中看到此信息,特别是下限或上限Date中的一个将不是您指定的内容。这意味着可能需要扫描大量文档才能完成查询,具体取决于您的数据。

要查找数组中两个边界之间有last_modified的对象,我尝试使用$elemMatch

db.orders.find({"suborders": {
    $elemMatch:{ 
        last_modified:{
            "$gt":ISODate("2016-09-15T00:00:00Z"), 
            "$lt":ISODate("2016-09-16T00:00:00Z")
         }
     }
}})

在我的测试中,这回复了约0.5%的所有文件。但是,它仍然运行缓慢。 explain输出显示它仍未正确设置索引边界(仅使用一个边界)。

最终效果最好的是使用minmax手动设置索引范围。

db.subDocs.find()
    .min({"suborders.last_modified":ISODate("2016-09-15T00:00:00Z")})
    .max({"suborders.last_modified":ISODate("2016-09-16T00:00:00Z")})

返回了与$elemMatch相同的文档,但在索引上使用了两个边界。对于elemMatch和原始发现,它的运行时间为0.021s,而2-4s则为。

相关问题