我有这样的查询,我想按日期对结果进行排序。我在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
}
答案 0 :(得分:2)
因为默认情况下_id
字段上的MongoDb creates an unique index是您在排序为fast
=> { "UserId" : 1}
时使用的字段。
在DateTime
上添加索引应该有助于提高速度。
答案 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
标准可能更具选择性,因此要扫描的文档更少。
同时支持两个查询的理想索引应该是包含支持所需排序顺序的DateTime
和UserId
的复合索引。例如: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的较早版本的解释输出)。