使用 Mongoose 跳过大量记录时如何避免内存限制?

Max*_*lin 4 indexing mongoose mongodb

在具有超过 10 万条记录的集合中,当我使用 Mongoose 选项进行查询时,如下所示:

contact.find({}, {}, {
  collation: {
    locale: 'en_US',
    strength: 1
  },
  skip: 90000,
  limit: 10,
  sort: {
    email: 1
  }
});
Run Code Online (Sandbox Code Playgroud)

我收到此错误:

MongoError:查找命令期间执行程序错误:OperationFailed:排序操作使用的 RAM 超过最大 33554432 字节。添加索引,或指定较小的限制。

但我确实在电子邮件字段上有一个索引:

{
  "v" : 2,
  "key" : {
    "email" : 1
  },
  "name" : "email_1",
    "ns" : "leadfox.contact",
    "background" : true
}
Run Code Online (Sandbox Code Playgroud)

另一方面,当我在 Mongo shell 中查询时,它工作正常:

db.contact.find().sort({email: 1}).skip(90000).limit(10)
Run Code Online (Sandbox Code Playgroud)

bar*_*ini 6

你所经历的是因为skip。正如您在文档中看到的

cursor.skip() 方法通常很昂贵,因为它要求服务器在开始返回结果之前从集合或索引的开始处走来获取偏移量或跳过位置。随着偏移量(例如上面的 pageNumber)的增加,cursor.skip() 将变得更慢并且更占用 CPU。对于较大的集合, cursor.skip() 可能会成为 IO 绑定。

您应该找到更好的方法而不是跳过。当您使用email字段对文档进行排序时,您可以使用电子邮件字段编写范围查询,而不是skip这样:

contact.find({ "email": { $gt: the_last_email_from_previous_query } }, {}, {
  collation: {
    locale: 'en_US',
    strength: 1
  },
  limit: 10,
  sort: {
    email: 1
  }
});
Run Code Online (Sandbox Code Playgroud)

更新:

首先。就像我上面说的,你想要的是不可能的。Mongodb 是这么说的,不是我说的。

其次,我建议您搜索现代分页方法和人们的用例。你在评论中的例子是荒谬的。没有用户应该/不会直接转到第 790 页以获取任何数据。如果他们直接进入这样的页面,这很可能意味着,他们将数据覆盖到第 790 页,并且他们想继续。因此,即使您正在构建一个无状态系统(就像当今所有现代系统一样),您也应该为分页数据存储有关用户最后一个观点的一些信息。这是基于用户行为的示例方法(我不是说最好,这只是一个示例)。

另一种方法,您可以使用(像大多数现代分页表一样)您只允许用户向前或向后导航 5-6 页。所以,你可以跳过只有50-60在查询与合并文件$gt$lt有关email领域。

另一种方法是使用其他一些工具将数据缓存在内存中。

我想你明白了。快乐编码。