首页/数据/NoSQL 速查/游标 sort / limit / skip 与翻页
MongoDB99 条

游标 sort / limit / skip 与翻页

在游标上链式调用 sort、limit、skip;mongosh 里可用 it 继续取下一批,toArray() 一次取完(注意内存占用)。

NoSQL 速查数据更新于 2026-10-13

游标与翻页

find() 返回的不是数组而是游标:服务端按批取数据,客户端按需读取。sort / skip / limit 挂在游标尾部,写法像客户端排序,实际由服务端执行(mongos 会把排序下推到分片)。排序本身是内存动作,命中索引才能免排序成本。

基本信息

适用版本MongoDB 4.x-8.x / mongosh
语法db.<集合>.find(条件).sort({字段:1|-1}).skip(n).limit(m)
默认输出mongosh 只打印前 20 条
排序内存上限100MB,超出报错
所需权限read

典型使用场景

  • 列表页按时间倒序取最新 N 条:sort({ts:-1}).limit(20),最常用的组合
  • 后台跳页浏览:skip+limit,页码不大时够用且写法简单
  • 全量导出:用 hasNext/next 分批读,避免 toArray() 把结果全塞进内存
  • 配合 projection 只取需要的字段再排序:减少网络传输与反序列化开销
  • 分片集群按非分片键排序:需要 mongos 归并各分片结果,要评估代价

常用写法

常用写法
db.orders.find({ status: "paid" }, { orderNo: 1, ts: 1 })  # 条件 + 只投影两列
  .sort({ ts: -1, _id: -1 })      # 时间倒序,_id 兜底让同秒记录顺序稳定
  .limit(20)                      # 只返回 20 条

const c = db.orders.find().sort({ ts: -1 }).skip(100).limit(10)  # 每页 10 条,取第 11 页
c.toArray()                        # 一次读完(数据量大时慎用)
c.hasNext()                        # 判断是否还有下一条
it                                 # mongosh 中继续打印下一批

游标方法对照

方法作用注意点
sort({f:1})排序,1 升序 -1 降序,多字段按书写顺序比较未命中索引时走内存排序,超 100MB 报错
limit(n)限制返回条数limit(0) 表示不限制,不是返回 0 条
skip(n)跳过前 n 条服务端仍要扫描并丢弃这 n 条,越翻越慢
batchSize(n)设置单批返回条数只影响网络分批,不改变结果集与顺序
hasNext / next逐条取数游标闲置默认 10 分钟超时失效

键集翻页(推荐)

键集翻页(推荐)
const p1 = db.orders.find({ status: "paid" })     # 第一页
  .sort({ ts: -1, _id: -1 }).limit(20).toArray()
const last = p1[p1.length - 1]     # 记住本页最后一条,作为下一页的起点

db.orders.find({ status: "paid",                  # 下一页:不用 skip,翻页开销恒定
  $or: [ { ts: { $lt: last.ts } },
         { ts: last.ts, _id: { $lt: last._id } } ] })
  .sort({ ts: -1, _id: -1 }).limit(20)             # 数据增删也不会像 skip 那样错页
排序未命中索引且数据量超过 100MB 时查询直接失败(QueryExceededMemoryLimit);正解是建匹配排序顺序的复合索引,allowDiskUse 只是兜底(MongoDB 4.4 起 find 也支持该选项)。
mongosh 默认打印前 20 条,输入 it 打印下一批;官方驱动不会自动打印,返回的是游标对象,必须自己 for await 或 toArray 消费,两者的“翻页”手感不同。
带条件的排序尽量让索引前缀覆盖过滤字段,例如查询 status+ts 排序,建 {status:1, ts:-1} 复合索引可同时付过滤与排序。

易错点

  • 深分页用大 skip:服务端照样扫描并丢弃前 N 条,页码越大越慢 → 改用 (ts,_id) 键集翻页
  • 单字段排序遇到重复值顺序不保证,两次查询结果顺序可能不一致 → 加 _id 作次级排序键
  • 把 limit(0) 当成“返回 0 条”,实际含义是不限制,一次拉回全集合
  • 无索引排序超 100MB 直接报错中断查询 → 补复合索引或临时 allowDiskUse
  • 长时间遍历游标不控制节奏会碰到默认 10 分钟闲置超时 → 加大 batchSize 或分批重开游标,noCursorTimeout 需谨慎使用

数据来源与参考

  • MongoDB Manual: Cursor Methods
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌