avatar

目录
mongo 分页查询优化

今天接到个需求,要求起个定时 job,顺序每天取一定数量的 mongo 数据,但每天的数据不能重复,且每天的取用量不同。
假设今天取 100 个,明天从第 101 个开始取 205 个,这里面的数字,完全是运营给的一个随机数字,mongo的数据量还特别大。
怎么取用可难倒了我,一般的分页查询也不能用啊。

研究了一下,mongo 的分页查询,特别是大量数据的查询,简单记录如下。

一般常用的 mongo 分页,我们使用的是skip + limit的方式。但是数据量特别大的时候,就显得很低效了。

传统 mongo 分页

假设一页有 10 条记录,则
plaintext
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// page 1
1~10
// page 2
11~20
// page 3
21~30
// page n
10*(n-1)+ 1 ~ 10*n
```
mongoDB 提供了 `skip()` 和`limit()` 方法。

`skip`:跳过指定数量的数据,可以用来跳过当前页面的数据,即跳过`pageSize * (n-1)`.

`limit`:指定从mongoDB 中读取的记录条数,可以当做页面大小 `pageSize`.

所以针对上面的举例,分页就可以这样做:
```sql
// page 1
db.getCollection('file').find({}).limit(10)
// Page 2
db.getCollection('file').find({}).skip(10).limit(10)
// Page 3
db.getCollection('file').find({}).skip(20).limit(10)

存在问题

官方文档对 skip的描述:

skip 方法从结果集的开头进行扫描后返回查询结果。这样随着偏移的增加,skip将变得更慢。
所以需要一种更快的方式,其实和mysql数据量大之后不推荐使用limit一样的道理。

官方建议使用范围查询,可以使用索引分页,偏移量增加时通常会产生更好的性能。即指定开始位置解决方案是先查出当前页的第一条,然后顺序
pageSize条。

指定范围分页介绍

我们假设基于_id的条件进行查询比较。事实上,这个比较的基准字段可以是任何你想要有序的字段,比方说时间戳等等。

sql
1
2
3
4
5
6
7
// page 1
db.getCollection('file').find({}).limit(10)
// 找到并记录下最后一条记录的 objectID
lastObjectID = ObjectId("62a2c1e28e29b09fbf010104")

// page 2
db.getCollection('file').find("{"_id":{$gt:ObjectId("62a2c1e28e29b09fbf010104")}}").limit(10)
文章作者: Viola Tangxl
文章链接: https://violatangxl.github.io/2022/06/21/mongo-limit/
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 椰子是只猫
打赏
  • 微信
    微信
  • 支付宝
    支付宝

评论