首页/数据/NoSQL 速查/更新操作符 $set / $unset / $inc / $rename
MongoDB99 条

更新操作符 $set / $unset / $inc / $rename

局部修改字段的四件套:$set 赋值或新增、$unset 删除字段、$inc 原子自增、$rename 重命名字段;比整文档替换更安全。

NoSQL 速查数据更新于 2026-10-13

更新操作符是什么

$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 数量
  • 生产变更先在预发环境用相近规模数据演练一次
  • 记录变更条件与影响条数,便于事后追溯与复盘

易错点

  • $rename 不能作用于数组元素内的字段,对分片集合也有额外限制,迁移前先在小集合验证
  • $inc 只能用于数值类型,对字符串或缺失字段使用时行为与预期不同,缺失字段会按 0 起算
  • 把整段子对象用 $set 覆盖会丢掉子对象里没写到的字段,只想改一个键要用点号路径
  • updateMany 不带条件等价于全集合改写,线上执行前必须先用相同条件统计命中数量
  • 更新条件命中索引时走索引,未命中时逐文档扫描,大集合上会长时间占用资源

数据来源与参考

  • MongoDB Manual: Update Operators
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌