bulkWrite 中多个 updateOne 操作需并列写入 operations 数组,各自独立指定 filter 和 update;避免混用 $set 与 $setOnInsert,不用 updateMany + $cond 分流;ordered: false 适用于无依赖操作;性能关键在 filter 索引和批量大小控制。

bulkWrite 里怎么写多个 updateOne 操作
直接在 bulkWrite 的 operations 数组里并列写多个 updateOne 对象,每个对象独立指定 filter 和 update。MongoDB 不要求这些操作针对同一字段或结构,只要每个 filter 能准确定位文档即可。
常见错误是把多个字段更新塞进同一个 updateOne 的 $set 里,结果所有匹配文档都被套用同一套字段值——这根本不是“差异化”,只是批量覆盖。
- 每个
updateOne必须带自己的filter(比如{ _id: ObjectId("...") }或{ status: "pending" }) -
update部分可自由组合$set、$unset、$inc等,不同操作之间互不影响 - 避免在单个
updateOne中混用$set和$setOnInsert除非明确需要 upsert 行为
为什么不用 updateMany + $cond 做字段级分流
有人试图用 updateMany 配合 $cond 在一个更新语句里“条件赋值”,比如根据 type 字段决定设 price 还是 weight。这种写法看似简洁,实际问题很多:
- 所有匹配文档都会执行整个表达式,即使只改一个字段也要遍历所有
$cond分支,CPU 开销大 - 一旦
$cond逻辑出错(比如漏写else),整条更新可能静默失败或设成null - 无法单独控制每个文档的更新行为,比如某个文档要
$inc而另一个要$set,$cond无能为力 - 调试困难:出错时看不出是哪个分支导致了异常值
bulkWrite 的 ordered: false 要不要开
默认 ordered: true,遇到第一个失败操作就中止后续;设为 false 则继续执行其余操作。是否开启取决于你的容错需求:
- 如果各 updateOne 之间无依赖(比如更新不同用户的订单状态),开
ordered: false能提高成功率,失败项会出现在返回的writeErrors里,不影响其他更新 - 如果某次更新的结果是下一次更新的条件(极少见),必须保持
ordered: true,否则逻辑错乱 - 注意:即使
ordered: false,单个updateOne内部的原子性不变,不会出现“部分字段更新成功”
性能瓶颈常卡在 filter 索引和文档大小上
bulkWrite 本身不慢,但实际耗时往往来自两处:
-
filter字段没建索引:比如用{ createdAt: { $lt: ... } }更新几百条,却没在createdAt上建索引,每次都要全表扫 - 单个文档过大(>16MB)或批量提交太多操作(如一次传 10000 个
updateOne):驱动会自动分片,但网络和内存压力陡增;建议单次bulkWrite控制在 100–1000 条之间 - 避免在
update中使用复杂聚合表达式(如嵌套$map+$reduce),这类操作在服务端解析成本高,且无法利用索引加速
真正差异化更新的核心,是让每个 updateOne 各自负责、各自索引、各自轻量——而不是堆逻辑到一个操作里硬扛。











