MongoDB Bulk Write 是什么?ordered 和 unordered 有什么区别?
简化版
Bulk Write 可以把多条插入、更新、删除请求批量提交,减少网络往返并提升吞吐。ordered:true 遇到错误会按顺序停止,ordered:false 会尽量继续执行后续操作。
详细版
批量导入、批量修复、消息落库等场景,如果一条一条写 MongoDB,网络往返和驱动开销会很明显。Bulk Write 把多个写操作打包提交。
ordered:true保证按顺序执行,遇到第一个错误后停止后续操作。ordered:false不强依赖顺序,某些操作失败后仍会继续尝试其他操作。- unordered 更适合互不依赖的大批量写入,吞吐通常更好。
- 错误处理要看每条操作结果,不能只看整个批次成功或失败。
- 批次大小要控制,太大可能导致内存、网络包和锁竞争问题。
完整版教学
一、为什么批量写入能提升吞吐
数据库写入不只有服务端执行成本,还有客户端编码、网络传输、服务端解析和响应返回成本。单条写 1 万次,就可能有 1 万次网络往返;批量写每 1000 条提交一次,网络往返可能降到 10 次。Bulk Write 的核心价值就是把大量小请求合并成较少的大请求,减少往返和协议开销。对于导入数据、批量状态更新、补偿脚本,这个收益非常直接。
await db.collection("users").bulkWrite([
{ insertOne: { document: { name: "A" } } },
{ updateOne: { filter: { name: "B" }, update: { $set: { active: true } } } },
{ deleteOne: { filter: { name: "C" } } }
])
记忆钩子:Bulk Write 不是让每条写神奇变快,而是把“来回跑 1 万趟”改成“分批跑几趟”。
二、ordered 的语义:顺序和失败即停
ordered:true 表示操作按数组顺序执行,遇到错误后停止执行后面的操作。它适合后一个操作依赖前一个操作结果的场景,比如先插入主记录,再更新某个依赖状态。缺点是一个中间错误会阻断整个批次后续操作,吞吐和容错性较差。默认 ordered 行为虽然安全直观,但不一定适合大批量导入。
await users.bulkWrite(ops, { ordered: true })
三、unordered 的语义:尽量执行更多操作
ordered:false 表示批次中的操作不要求严格顺序,某些操作失败后,MongoDB 仍会尽量执行其他操作。它适合互相独立的写入,例如导入 10 万个用户,其中有少量重复手机号,不应该让一条重复阻断后面 99999 条。unordered 的结果处理更重要,因为你需要从批量结果中识别哪些成功、哪些失败,再决定是否重试或记录错误。
await users.bulkWrite(ops, { ordered: false })
四、带数字比较网络往返
假设应用到数据库一次请求平均网络和协议开销 2ms,不考虑服务端写入成本。逐条写 10000 条,仅往返开销就可能接近 20 秒;如果每 1000 条一批,只需要 10 次往返,开销约 20ms。当然真实写入还受磁盘、索引和复制影响,但这个数字说明了批量写为什么在导入场景里差距巨大。
逐条写:10000 × 2ms = 20000ms
1000 条/批:10 × 2ms = 20ms
往返开销差距:约 1000 倍
五、批量写不等于事务
Bulk Write 只是批量提交操作,不自动保证整个批次原子成功。ordered 停止后,前面成功的操作不会因为后面失败而自动回滚;unordered 更是可能部分成功、部分失败。如果业务要求全成功或全失败,需要显式使用事务,并接受事务带来的性能成本。很多导入场景其实允许部分成功,然后输出错误报告,这时 bulk write 比事务更合适。
| 能力 | Bulk Write | Transaction |
|---|---|---|
| 减少网络往返 | 是 | 不一定 |
| 整批原子性 | 否 | 是 |
| 适合大批导入 | 是 | 谨慎 |
| 错误处理 | 逐条分析 | 提交/回滚 |
六、批次大小和索引成本也要控制
批次不是越大越好。太大的批次会占用更多客户端内存、网络传输缓冲和服务端处理资源,也可能导致单次失败影响范围过大。经验上会按数据大小和写入耗时分批,比如每 500、1000 或 5000 条一批,再通过监控调整。还要注意索引数量:每条写入都要维护相关索引,批量写只能减少请求开销,不能消除索引维护成本。
调优顺序:
确定单条大小 -> 选择批次大小 -> 观察延迟/错误/复制延迟 -> 调整并发和批次
七、常见误区与追问
- 误区:Bulk Write 会保证整批原子。 它不是事务,批次可能部分成功。
- 误区:ordered:false 就是不可靠。 它适合互不依赖操作,能避免一条错误阻断整个导入。
- 误区:批次越大越快。 太大可能带来内存、网络包和服务端压力,应该按监控调。
- 追问:重复键错误怎么处理? unordered 导入中可以记录 Duplicate Key 的条目,成功的继续保留,失败的单独报告。
- 追问:什么时候用事务而不是 bulk? 当业务要求多条写入全成功或全失败时,使用事务更合适。
八、加强记忆
Bulk Write 记成“批量打包发货”:ordered 是按清单顺序发,中途出错就停;unordered 是能发的先发,坏件单独记账。它提升的是吞吐和网络效率,不提供整批事务语义。面试时讲清 ordered/unordered、部分成功、批次大小和错误处理,就能体现生产意识。