$ sort使我的查询在MongoDB中太慢

时间:2018-08-25 17:47:23

标签: mongodb mongodb-query aggregation-framework

我有这样的查询,我想按日期对结果进行排序。我在DateTime上有一个降序索引,而在UserId上有一个升序索引,但是当我尝试按DateTime对结果进行排序时,它变得太慢了。

db.Users.aggregate([  
  { "$match" : { "UserId" : { "$in" : [NUUID("1b029f8b-a17e-3172-9247- 
                  9cddfaf9702b")] } } },       
  { "$match" : { "DateTime" : { "$gte" : ISODate("2018-08-15T12:54:38Z"), 
    "$lte" : ISODate("2018-08-25T12:54:38Z") } } },   
  { "$sort" : { "DateTime" : -1} }, { "$skip" : 0 }, { "$limit" : 20 }])

当我删除排序部分时,它变得太快了。我尝试如下,而且速度也太快了。

db.Users.aggregate([             
  { "$match" : { "DateTime" : { "$gte" : ISODate("2018-08-15T12:54:38Z"), 
    "$lte" : ISODate("2018-08-25T12:54:38Z") } } }, 
  { "$match" : { "UserId" : { "$in" : [NUUID("1b029f8b-a17e-3172-9247- 
     9cddfaf9702b")] } } },  
  { "$sort" : { "UserId" : 1} },{ "$skip" : 0 }, { "$limit" : 20 }])

为什么只有当我想按DateTime排序时它才慢? 这是我文件的结构

{
    "_id" : NUUID("11111111-1111-1111-1111-629f7992f895"),
    "DateTime" : ISODate("2018-08-23T15:49:51.153Z"),
    "UserId" : NUUID("aaaaaaaa-aaaa-aaaa-9247-9cddfaf9702b"),
    "PostId" : NUUID("bbbbbbbb-bbbb-bbbb-9529-d49ae48b2604"),
    "Type" : 3
}

3 个答案:

答案 0 :(得分:2)

因为默认情况下_id字段上的MongoDb creates an unique index是您在排序为fast => { "UserId" : 1}时使用的字段。

DateTime上添加索引应该有助于提高速度。

这里有一些considerations when it comes to sorting fields

答案 1 :(得分:1)

为查询中使用的属性添加索引。

Mongo需要一个索引来通过给定属性有效地对数据进行排序或匹配。没有它,Mongo必须访问集合中的每个文档以检查所述属性的值。

以您为例,您要确保在此聚合的UserId和DateTime上有一个索引。

看到您有一个PostId(我想可以用来执行查询),您也应该为其添加索引。

您可能还希望查看复合索引=> https://docs.mongodb.com/manual/core/index-compound

答案 2 :(得分:1)

第一个查询的性能问题是您在DateTime(降序)和UserId(升序)上创建了单独的索引。当排序操作与谓词完全分开时,MongoDB(从4.0开始)无法使用index intersection对查询结果进行排序,因此,如果这些是唯一可用的候选索引,则只能选择一个。

注意:尽管您的源管道中有两个$match阶段,但是MongoDB服务器将coalesce these into a single $match stage,这是使用$and的等效查询。

  

为什么仅当我想按DateTime排序时,速度慢?

在内存中排序结果被认为是一项昂贵的操作,除非您在聚合中添加aggregation stage memory limit (100MB)选项,否则就不能超过allowDiskUse。与MongoDB 4.0一样,查询计划程序没有有关索引基数的统计信息,因此聚合将倾向于支持有效排序(在您的情况下为DateTime的索引计划)。第一个查询的结果将是索引扫描,以查找所有匹配的DateTime值(按排序顺序),并与每个符合UserId标准的匹配文档进行比较。

在按UserId排序的第二个查询中,UserId索引可用于匹配和排序结果。仍然需要为DateTime过滤结果,但是UserId标准可能更具选择性,因此要扫描的文档更少。

同时支持两个查询的理想索引应该是包含支持所需排序顺序的DateTimeUserId的复合索引。例如:db.Users.createIndex({ UserId: 1, DateTime: -1})。如果添加此复合索引,由于prefix of the compound index可以有效回答相同的查询,因此您也可以删除原始的{ UserId:1}索引。

了解查询性能的最直接方法是使用explain executionStats进行聚合查询。对于聚合管道,这一级别的详细解释需要MongoDB 3.6+;对于较旧的服务器版本,您可以解释等效的find()查询。您的聚合查询当前不包含标准find()查询中无法表达的任何处理阶段。

有关更多信息,请参阅MongoDB文档中的Use Indexes to Sort Query Results。博客文章Optimizing MongoDB Compound Indexes也有一些有用的背景知识(尽管使用了MongoDB的较早版本的解释输出)。