$inc不会丢失更新,它只保证单文档内字段原子递增;所谓“丢失”实为误判:modifiedCount=1不意味值变化(如$inc:{n:0}),需查旧值再比对,且须防字段名错、类型错、并发写热点。

$inc 本身不会丢失更新,但“看起来丢”是因为你没看清它在做什么
真正的问题不是 $inc 失效,而是你误以为它能解决所有并发场景——而它只保证单文档内字段的原子递增,不负责跨文档协调、不防重复初始化、也不自动规避写入热点。
为什么 $inc 返回 modifiedCount: 1 却查不到值变化?
这是最常被当成“丢失更新”的假象。$inc 操作只要匹配到文档,哪怕增量为 0(比如 $inc: { clicks: 0 }),MongoDB 也会计为一次修改,modifiedCount 就是 1。但它根本没改值。
- 别只信
modifiedCount,要查原始值再比对:先find取出旧值,执行$inc,立刻再find看是否真变了 - 读操作必须带
readConcern: "majority",否则可能读到未提交或回滚前的旧快照 - 如果用了
upsert: true但查询条件太宽(如只用{ status: "active" }),可能意外创建多个文档,导致计数分散
并发写同一文档时,$inc 真的会冲突吗?
不会。WiredTiger 的文档级 MVCC 保证了 $inc 在单文档上是严格原子的——1000 个并发同时对 { _id: "page_home" } 执行 $inc: { count: 1 },最终结果一定是初始值 + 1000。
- 但前提是:所有请求都打到同一个文档(
_id或查询条件完全一致) - 如果查询条件用了非唯一字段(如
{ category: "news" }),可能匹配到多个文档,updateOne只改第一个,其余被忽略——这不是 $inc 的问题,是查询逻辑错 - 高并发下单文档写入会成为瓶颈:WiredTiger 文档锁虽细,但锁竞争仍会导致 write conflict,触发重试甚至超时;这时你会看到大量
WriteConflictException
什么情况下 $inc 会“静默失效”?
不是失败,而是做了你没意识到的事——最典型的是字段名拼错或类型错位。
- 字段名写成
scroe而非score:$inc会静默创建新字段,后续查询score为空,但没人报错 - 字段原值是字符串
"123"或数组[1,2],执行$inc直接抛Cannot increment with non-numeric value错误,但若没捕获异常,就表现为“没更新” - 字段初始值是
null或undefined,$inc会当作 0 处理;但如果之前用$set设过null,而 schema 层没校验,就埋下隐患 - 混用数字类型:
$inc: { n: 1 }写入 Int32,$inc: { n: 1.0 }写入 Double,聚合时可能因类型不一致导致精度丢失或索引失效
字段名校验、数值类型统一、写入后显式验证值变化——这三件事漏掉任何一项,$inc 就从原子保障变成数据幻觉的源头。


















