MongoDB 入门教程
排序、限制与分页
本教程共 50 篇 · 第 21 篇 · 更新于 2026-07-30 · 约 7 分钟阅读
MongoDBMongoDB 入门教程排序分页sort深分页
21. 排序、限制与分页
本节目标:学会用 sort、limit、skip 给查询结果排序和翻页,并理解为什么「跳过很多条」的深分页会很慢。
查出来的文档默认按 _id 顺序返回,不一定是你想要的顺序。排序(sort)、限制(limit)、跳过(skip)这三个方法就是用来收拾结果的。它们都接在 find 后面,用点号串起来。
db.users.insertMany([
{ _id: 1, name: "张三", age: 28, city: "北京" },
{ _id: 2, name: "李四", age: 34, city: "上海" },
{ _id: 3, name: "王五", age: 22, city: "北京" },
{ _id: 4, name: "赵六", age: 41, city: "广州" },
{ _id: 5, name: "孙七", age: 30, city: "上海" }
])
排序:sort()
sort() 接收一个文档,字段写 1 表示升序、-1 表示降序:
// 按年龄从大到小排
db.users.find({}, { name: 1, age: 1, _id: 0 }).sort({ age: -1 })
多个字段也可以排,前面的优先:
// 先按城市升序,城市相同再按年龄降序
db.users.find({}, { name: 1, city: 1, age: 1, _id: 0 })
.sort({ city: 1, age: -1 })
Note排序方向只认
1和-1。想按「最近注册」排,就对registered_at这样的日期字段降序即可。
限制条数:limit()
limit(n) 只保留前 n 条结果,常用来取「排行榜前几名」:
// 年龄最大的前两名
db.users.find({}, { name: 1, age: 1, _id: 0 })
.sort({ age: -1 }).limit(2)
跳过条数:skip()
skip(n) 跳过前 n 条,常和 limit 搭配做分页:
// 第 2 页,每页 2 条(跳过前 2 条,再取 2 条)
db.users.find({}, { name: 1, age: 1, _id: 0 })
.sort({ age: -1 }).skip(2).limit(2)
用 sort + skip + limit 做分页
经典分页套路就是「固定每页大小,靠 skip 移动起点」:
| 页码 | skip | limit | 含义 |
|---|---|---|---|
| 第 1 页 | 0 | 2 | 取第 1–2 条 |
| 第 2 页 | 2 | 2 | 取第 3–4 条 |
| 第 3 页 | 4 | 2 | 取第 5–6 条 |
公式:skip = (页码 - 1) * 每页大小。
深分页的性能坑
skip 不是「直接跳到那一行」,而是 MongoDB 先扫出前面所有被跳过的文档再丢掉。翻到第 1000 页时,它其实读了前面 2000 条:
// 深分页:skip 很大,越往后越慢
db.orders.find({}).sort({ created_at: -1 }).skip(20000).limit(20)
Warning深分页(大 skip)会随偏移量增大而明显变慢,因为它要扫描并丢弃大量文档。这是我在生产环境反复踩过的坑。
深分页的替代思路
真要做无限滚动或深翻页,更好的办法是用「上一页最后一个值」当游标。比如按时间倒序翻页:
// 第一页
db.orders.find({}).sort({ created_at: -1 }).limit(20)
// 下一页:只取比上一页最后时间更早的,彻底不要 skip
db.orders.find({ created_at: { $lt: ISODate("2024-06-12") } })
.sort({ created_at: -1 }).limit(20)
这种「基于游标」的分页只靠索引定位,不丢数据,速度恒定。
Tip小数据量用 skip 翻页没问题;一旦数据量大、要翻很深,换成「时间戳/ID 游标」分页才是正道。
排序、限制、分页就讲到这。下面我们进入更新(Update),先认识一批更新运算符。