游标是懒加载的
查询返回的不是结果数组而是游标:数据按批次从服务端取回,客户端逐条消费。hasNext/next 适合边取边处理,toArray 一次性读进内存,batchSize 控制每次网络往返取多少条。理解批次机制很重要——它决定了内存占用与网络往返次数,也是「在命令行里只看到前 20 条」这类现象的来源。
基本信息
| 逐条遍历 | while (c.hasNext()) { printjson(c.next()) } |
|---|---|
| 一次取完 | c.toArray(),结果全部进内存 |
| 批量大小 | c.batchSize(200),控制每次网络取回条数 |
| 超时控制 | c.maxTimeMS(3000)、c.noCursorTimeout() |
| 当前批次剩余 | c.objsLeftInBatch() |
| 版本提示 | cursor.count() 已废弃,改用 countDocuments 或 estimatedDocumentCount |
典型使用场景
- 导出与迁移:用游标分批拉取,避免一次性把大结果读进内存
- 在线批处理:配合 batchSize 与 maxTimeMS 控制单批资源占用
- 命令行排查:默认只打印首批数据,确认总量必须显式计数
- 慢查询定位:对游标调用 explain 查看扫描量与批次行为
- 长时间任务:给游标设置合理超时,任务中断后能被及时发现
四种遍历方式
四种遍历方式
// 逐条:内存占用最小,适合边取边处理
const c = db.logs.find({ level: "error" }).batchSize(200)
while (c.hasNext()) { printjson(c.next()) }
// 一次性取完:只在结果集确定较小、且后续需要随机访问时使用
const arr = db.users.find({ vip: true }).limit(100).toArray()
print(arr.length)
// 回调式遍历:内部仍按批次取数据,写法更紧凑
db.logs.find({ level: "warn" }).forEach(function (doc) { print(doc._id) })
// 确定总量用计数方法,而不是看游标打印出的条数
const n = db.logs.countDocuments({ level: "error" })方法与行为读法
| 方法 | 作用 | 注意点 |
|---|---|---|
| hasNext / next | 逐条判断与取值 | 遍历期间数据被修改可能影响后续批次 |
| toArray | 全部读入内存 | 大结果集会造成内存压力甚至进程被终止 |
| batchSize | 设置每次网络取回条数 | 设得过大单批内存高,过小则往返次数增加 |
| limit / skip | 限制返回条数与偏移 | 深偏移代价高,顺序翻页更适合键集方式 |
| maxTimeMS | 限制游标执行时长 | 超时是错误而非空结果,要按错误处理 |
| objsLeftInBatch | 当前批次剩余条数 | 用于确认批次行为与调试 |
默认不返回全部数据是设计而非缺陷:命令行打印游标通常只显示前一批文档,需要总数时要显式计数,否则很容易把「只有 20 条」当结论。
toArray 会把结果全量读入客户端内存:导出或统计类任务应逐批处理或直接聚合,不要在大集合上使用。