【问题标题】:$SKIP stage contain all previous records$SKIP 阶段包含所有以前的记录
【发布时间】:2021-05-20 16:22:02
【问题描述】:

我有一组1.2M 文档,我正在使用pagination(每页25 个)列出它们。为此,我分别使用Aggregation Framework$sort->$skip->$limit->$project 阶段。一切正常,但$skip 的奇怪行为导致nReturned 文档具有很高的价值,因为我一直在浏览页面。

例如,假设 $limit => 25,第 1 页 => nReturned 25, Skip 0, Display 1-25,在第 2 页 => nReturned 50, Skip 25, Display 26-50,在第 3 页 => nReturned 75, Skip 50, Display 51-75 等等...和在第 LAST => @987654331 @

如果我访问最后几页,nReturned, totalDocsExamined and totalKeysExamined 将接近 120 万,因为我有很多记录。

查询

db.collection.aggregate([
{
    $sort : {
        date_added : -1
    }
},
{
    $skip : 50  
},
{
    $limit : 25
},
{
    $project : {
        luid : 1,
        name : 1
    }
}
])

我做错了什么还是有什么办法可以优化这个查询?

【问题讨论】:

  • 我忘了提到我在date_added : -1上使用索引

标签: mongodb mongoose aggregation-framework


【解决方案1】:

使用$skip 的内存效率非常低,如果要转到最后一页,则会跳过 120 万条记录。

你真的会在你的分页下显示第 10000 页还是会有 <> 箭头?

更好的方法是使用最后一个键,例如以下记录。

{ 
   _id: 1
   _data: ...
},
{   
   _id: 2
   data: ...
}....

在第一页,我的最后一个键将是 25,第二页,我的最后一个键将是 50,或者例如62,如果我的键不按顺序(中间删除)。

这样我的分页就变得很简单了

Model.find({ _id: { $gt: last }}).limit(25).sort({ _id: 1 })

last 是在第 1 页中检索到的最后一个 id。

调用上一页就像将$gt切换到$lt并反转排序一样简单

提示:在大多数情况下,没有人会去选择页面,例如 100204,如果我们确实有这么多记录,我们通常会有搜索功能。

如果我们真的需要使用$skip,应该使用缓存层来缓存检索到的记录(例如使用redis),以防止重复调用相同的查询。

【讨论】:

  • 非常感谢您的建议。我已经实现了 SEARCH 功能,通常人们只使用它。
  • 正如您所建议的Redis,MongoDB 是NoSQL,Redis 也是。将 Redis 用于所有读取操作作为 Mongo 之上的一个层有什么好处?
  • Redis 仅充当缓存层来存储查询结果。所以你的算法类似于if (!redisCache) model.find(...)。您可以设置redis在一定时间(例如60秒)后使key过期,这样在这60秒内,您的mongodb查询只被调用一次,从而导致更快的响应,特别是对于需要很长时间执行的查询。
  • 想象一下,如果你有一个搜索 + 自动完成功能,搜索“算法”这个词将调用 mongodb 9 次。如果 10 个人在短时间内搜索同一个词,您将重复调用同一个繁重的查询 90 次。而如果添加缓存层,redis 可以在更短的响应时间内响应请求。
猜你喜欢
  • 1970-01-01
  • 2022-12-06
  • 2016-02-19
  • 2015-07-10
  • 2013-04-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多