batchsize 控制每次从 mongodb 拉取的文档数量,不影响结果集大小,仅优化网络往返;默认初始批次为101或16mib较小值,推荐设为1000,避免与skip/limit混淆。

batchSize 不是 limit,别混用
很多人一看到 batchSize() 就以为它像 limit() 那样控制最终返回多少文档——完全不是。它只控制每次网络请求从 MongoDB 服务端“拉”多少个文档到客户端内存里。游标整体结果集大小由查询本身(比如 find({}))决定,batchSize() 只影响“怎么分批拿”。
常见错误现象:设了 .batchSize(10) 却发现遍历完几万条数据花了好几秒,还以为是查询慢;其实真正瓶颈是发了上千次网络往返(round-trip),每次只取 10 条。
-
batchSize(0)表示创建游标但不取第一批数据(极少用,通常用于延迟初始化) - 默认初始批次是 101 个文档或 16 MiB 中较小者,后续批次上限固定为 16 MiB
- 设得太大(比如 50000)可能让驱动分配过多内存、触发 GC 或拖慢单次响应,尤其在低配客户端上
什么场景下必须调大 batchSize
典型高开销模式:循环中反复新建游标 + next() 取单条,比如按 ID 每隔 N 个取一个文档。这种写法本质是 N 次独立查询,索引查找+网络往返全重复。
正确做法是用一个大批次游标一次性拉取,再在内存里过滤。实测显示:batchSize(1000) 比默认值快 2–3 倍;batchSize(10000) 在千兆内网下通常已达吞吐拐点,再大收益递减甚至负向。
- 适用场景:全量扫描、ETL 导出、后台批量处理
- 不适用场景:交互式分页(此时应改用
_id范围分页,而非依赖batchSize) - 注意:Presto 的 MongoDB Connector 里对应配置项是
mongodb.cursor-batch-size,不是驱动层的batchSize()
batchSize 和内存、延迟的实际权衡
设成 1000 并不意味着你立刻占用了 1000 个文档的内存——MongoDB 驱动是流式消费的,cursor.next() 拿到一个就处理一个,下一批只在当前批耗尽时才发起请求。但批次越大,单次请求的序列化/反序列化压力越高,首字节延迟(time-to-first-byte)可能变长。
容易被忽略的点:如果你用的是 Node.js 的 mongodb 驱动,batchSize 必须在 find() 后立即链式调用,放在 sort() 或 skip() 后面会被忽略(驱动不报错但无效)。
- 推荐起步值:
batchSize(1000),适用于大多数万级到百万级集合 - 压测建议:用
cursor.explain("executionStats")看nReturned和totalDocsExamined是否匹配,确认没因批次太小导致重复扫描 - 线上慎用
batchSize(0)或超大值(>50000),某些旧版驱动在极端值下会静默降级为默认行为
和 skip/limit 分页的根本区别
有人试图用 batchSize 实现“跳过前 N 条”,这是无效的。batchSize 不跳数据,只管“每次拿多少”。真要分页,skip() 在大数据集上会越来越慢(因为每次都要数着跳过前面所有文档)。
替代方案是基于 _id 的游标分页:记录上一页最后的 _id,下一页查 { _id: { $gt: lastId } },再配合理想的 batchSize 控制拉取节奏。这样既避免 skip 的性能悬崖,又让网络更可控。
- 错误写法:
collection.find().skip(10000).limit(100).batchSize(100)(skip已经拖垮性能) - 正确组合:
collection.find({ _id: { $gt: lastId } }).sort({ _id: 1 }).batchSize(1000) - 关键提醒:
batchSize对聚合管道(aggregate())同样生效,但必须加在.cursor({ batchSize: 1000 })选项里,语法不同











