必须用 bulkWrite() 实现高效批量操作,因其单请求混合增删改、减少网络往返和事务开销;避免 insertMany()+updateMany() 拆分调用,禁用冗余 writeConcern,确保索引覆盖及分片键正确路由。

批量写入必须用 bulkWrite(),别再拼 insertMany() + updateMany()
MongoDB 8.0 的批量性能提升核心不在单个 API 调用速度,而在减少网络往返和事务开销。bulkWrite() 是唯一能在一个请求里混合插入、更新、删除、替换操作的接口,而 insertMany() 和 updateMany() 各自发一次请求,即使目标是同一集合,也会触发多次序列化、认证、日志写入。
- 混合操作场景(如导入时补全缺失字段+修正错误状态)必须用
bulkWrite(),否则会多出 2–3 倍 RTT 延迟 -
bulkWrite()默认启用有序执行(ordered: true),遇到第一个失败就停止;若允许部分成功,显式设为ordered: false,但注意错误位置需靠返回的writeErrors数组索引定位 - 8.0 对
bulkWrite()的内部批处理队列做了内存预分配优化,单次提交 1000 条比拆成 10 次 × 100 条快约 40%,尤其在高延迟网络下更明显
写关注(writeConcern)设成 { w: "majority" } 会拖慢批量速度
默认的 w: "majority" 要求多数节点确认,对批量操作是“放大器”:100 条写入 → 100 次 majority 等待。开发或内部服务可降级为 { w: 1 }(仅主节点确认),真实压测中吞吐量常提升 2–5 倍。
- 生产环境若需强一致性,优先考虑用事务包裹批量操作,而非依赖
writeConcern;事务在 8.0 中已支持跨分片批量,且 commit 阶段才做 majority 等待 - 避免在
bulkWrite()每个操作里单独设writeConcern—— 它会被顶层选项覆盖,徒增解析开销 - 如果集群启用了
journal: true(8.0 默认开启),w: 1实际已保证崩溃恢复不丢数据,不必盲目追求majority
索引缺失会让 updateMany() 或 deleteMany() 变成全表扫描
批量更新/删除没走索引,不是慢一点,而是直接卡死。MongoDB 8.0 的查询优化器虽更强,但不会自动为 updateMany({ status: "error" }) 这类条件建索引 —— 你得自己加。
- 用
explain("executionStats")查看updateMany()执行计划,重点盯totalDocsExamined是否等于nMatched;若远大于,说明没走索引 - 复合索引顺序很重要:比如
updateMany({ tenantId: "abc", status: "pending" }),索引必须是{ tenantId: 1, status: 1 },反过来无效 - 8.0 支持通配符索引(
{"$**": 1}),但仅适用于动态字段场景;对固定条件批量操作,明确字段索引性能更好、内存占用更低
分片集群下批量操作要小心路由键(shard key)分布
在分片集群中,bulkWrite() 请求若没带完整分片键,mongos 会广播到所有分片,造成 N 倍负载。8.0 的负载均衡器虽更智能,但无法规避这种误广播。
- 插入文档必须包含分片键字段,否则被路由到随机分片,后续按分片键查询时可能查不到
- 更新/删除操作的 filter 必须包含分片键等值条件(如
{ shardKey: "x123", ... }),范围查询({ shardKey: { $gt: ... } })会命中多个分片,但至少可控 - 用
db.runCommand({ getShardVersion: "coll" })确认当前分片版本,避免因配置未同步导致路由错误
updateMany() 正在扫千万级文档,或某次 bulkWrite() 因缺失分片键被发往全部 12 个分片 —— 这些细节在 8.0 里依然得手动把关。



















