更新操作符是什么
$set、$unset、$inc、$rename 是局部修改文档字段的四件套:$set 赋值或新增字段、$unset 删除字段、$inc 做原子自增减、$rename 重命名字段。它们只改动指定路径,比整文档替换安全得多,也不会覆盖并发写入的其他字段。$inc 与 $set 在单文档层面是原子操作,适合做计数与状态流转。
基本信息
| $set | 设置或新增字段,值可以是任意 BSON 类型 |
|---|---|
| $unset | 删除字段,值写什么都会被忽略 |
| $inc | 对数值字段原子加减,可为负 |
| $rename | 重命名字段,内部按先删后加实现 |
| 配合方法 | updateOne / updateMany / findOneAndUpdate |
| 原子性 | 单文档更新原子,多文档不保证整体原子 |
典型使用场景
- 状态流转:把订单从 pending 改成 paid,只动一个字段
- 计数器:$inc 累加浏览量与库存,避免读改写丢更新
- 字段清理:下线废弃字段用 $unset 批量删除
- 结构演进:字段改名后用 $rename 批量迁移到新命名
- 补默认值:给历史文档补充缺失字段
- 记录变更:$set 同时更新 updated_at 时间戳
示例
示例
db.orders.updateOne({ _id: 1001 }, { $set: { status: "shipped", updatedAt: new Date() }, $inc: { qty: -1 } })
db.orders.updateOne({ _id: 1001 }, { $unset: { remark: "" } }) # 删除 remark 字段
db.orders.updateMany({}, { $rename: { qty: "quantity" } }) # 全量改名(先核对命中数)
db.stats.updateOne({ _id: "views" }, { $inc: { n: 1 } }, { upsert: true }) # 不存在则新建操作符对照
| 操作符 | 作用 | 注意点 |
|---|---|---|
| $set | 赋值或新增字段 | 点号路径可写嵌套字段 |
| $unset | 删除指定字段 | 仅删字段不删文档 |
| $inc | 数值原子增减 | 只能用于数值类型 |
| $rename | 重命名字段 | 不支持数组元素内字段 |
| $setOnInsert | 仅 upsert 插入时生效 | 常与 $set 搭配 |
| $currentDate | 写入服务器当前时间 | 避免依赖客户端时钟 |
同一次更新里对同一路径同时写 $set 与 $unset 会触发 ConflictingUpdateOperators 报错,改字段前先想清楚是覆盖还是删除。
$inc 是原子操作,天然适合高并发计数;而「先 find 读出来再加一写回」在并发下会丢更新,两者不要混用在同一业务逻辑里。
更新实践要点
- 更新条件字段要有索引,否则 updateMany 是全集合扫描
- 执行前先用相同条件 countDocuments 核对命中范围
- 嵌套字段用点号路径 "a.b.c",不要手写整段子对象覆盖
- 给数组元素内字段改名要改用 $set 加位置操作符
- 涉及多文档一致性时用事务或分步校验,单条 updateMany 不保证整体原子
常见误用与替代写法
- 只想改嵌套对象里的一个键:用点号路径,不要整段覆盖子对象
- 想追加数组元素:用 $push,不要用 $set 覆盖整个数组
- 想返回更新后的文档:用 findOneAndUpdate 并指定 returnDocument: "after"
- 想只在插入时设默认值:配合 $setOnInsert 与 upsert 使用
- 想移除数组中的某个元素:用 $pull 而不是 $unset
- 想纯计数:用 $inc,不要读出来再写回
updateMany 的 matchedCount 与 modifiedCount 可能不一致:字段值本来就相同的文档会计入命中但不计入修改,对账时要按两个数字分别理解。
高并发下对同一文档反复 $inc 是安全的;而「先读出值再 $set 写回」会丢更新,计数器类字段应统一使用 $inc。
更新嵌套数组中的元素要先定位下标,位置操作符 $ 需要查询条件里明确给出数组匹配条件,否则会直接报错。
核对与回滚准备
- 更新前用相同条件统计命中数量作为基线,更新后再统计一次比对差值
- 需要回滚时依赖备份或操作前的字段快照,更新本身没有撤销
- 改结构前先抽样查看几种典型文档形态,避免只覆盖一部分数据
- 批量更新分批执行,每批结束核对 matched 与 modified 数量
- 生产变更先在预发环境用相近规模数据演练一次
- 记录变更条件与影响条数,便于事后追溯与复盘