首页/数据/NoSQL 速查/聚合中的 $sort / $limit / $skip
MongoDB99 条

聚合中的 $sort / $limit / $skip

排序、截断与跳过:$sort 后接 $limit 可表达 Top-N;注意排序阶段内存占用与大 skip 的代价。

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

$sort / $limit 是什么

聚合里的 $sort、$limit、$skip 负责排序与截断:$sort 按一个或多个字段排序(1 升序、-1 降序),$limit 取前 N 条,$skip 跳过前 N 条。$sort 紧跟 $limit 可以表达 Top-N,优化器会把「先全排序再截断」改写成只保留前 N 条的堆排序,内存占用显著下降;两者之间隔了其它阶段时这个改写不成立。排序阶段默认受 100MB 内存上限约束,超限直接报错。

基本信息

Top-N 写法$sort 之后紧跟 $limit
排序方向-1 降序,1 升序
内存上限单阶段默认 100MB,超出报错
溢写开关allowDiskUse 允许溢写磁盘换取内存
分页$skip + $limit 只适合小偏移
索引管道首段的排序与索引顺序一致时可免排序

典型使用场景

  • 榜单查询:按金额、积分或数量取前 N 名
  • 最新记录:按时间倒序取最近若干条事件
  • 明细分页:小偏移翻页,或用排序键做键集分页
  • 结果裁剪:聚合算完只返回需要展示的几行,减少网络传输
  • 分布核对:分别取最大与最小的几条,确认数据是否符合预期

写法与优化

写法与优化
db.orders.aggregate([
  { $match: { status: "paid" } },   // 先过滤,减少参与排序的文档数
  { $sort: { ts: -1 } },             // 按时间倒序
  { $limit: 3 }                      // 与 $sort 相邻才会走堆排序优化
])

// 小偏移分页(偏移很大时改用排序键范围条件)
db.orders.aggregate([ { $sort: { ts: -1 } }, { $skip: 20 }, { $limit: 20 } ])

// 排序占用超 100MB 时放行磁盘溢写,用速度换内存
db.orders.aggregate([
  { $sort: { amount: -1 } },
  { $limit: 100 }
], { allowDiskUse: true })

三个阶段对照

阶段作用代价
$sort排序,可与索引顺序一致时免排序内存占用,超 100MB 报错
$limit取前 N 条本身开销很小
$skip跳过前 N 条偏移越大越慢,前面的记录同样被处理
$sort + $limitTop-N,改写为堆排序两者必须相邻
allowDiskUse允许溢写磁盘缓解内存但整体更慢
管道最前面的 $match + $sort + $limit 若能命中索引,服务端可以直接按索引顺序读前 N 条,几乎不产生额外排序开销,这是榜单类查询最理想的形态。
$skip 十万这类深分页会让每一步都白扫大量文档,延迟随页数线性上升;换成带排序键的范围条件($lt 上一页末值)才能保持稳定延迟。

易错点

  • 大偏移分页在聚合里同样低效,改用带排序键的范围条件翻页
  • 以为 $limit 能省掉排序:只要它与 $sort 不相邻,前面整段数据仍要排完
  • 排序字段无索引且数据量大会撞上 100MB 上限报错,需要加索引或开 allowDiskUse
  • 多字段排序时字段顺序决定优先级,{ a: 1, b: -1 } 与 { b: -1, a: 1 } 得到的顺序不同

数据来源与参考

  • MongoDB Manual: $sort / $limit
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌