如何让MongoDB中使用$inc运算符的update命令具备幂等性?
实现MongoDB(AWS DocumentDB)中$inc更新的幂等性
MongoDB的update命令里,$inc、$mul、$add这类运算符不具备幂等性。比如users集合中有文档:
{"_id" : "123", "age" : 24, "name": "me"}
执行db.users.update({_id: "123"}, {$inc: {age: 1}})可将age加1,但如果意外执行两次(比如重试机制触发),age会被加2,不符合幂等要求。
已尝试的无效/有缺陷方案
方案1:用$set替换$inc
尝试通过查询当前值、计算新值后,用带条件的$set实现更新,但无法保证幂等性。
var doc = db.users.findOne({_id: "123"}) var oldAge = doc.age var newAge = oldAge + 1 db.users.updateOne({_id: "123", age: oldAge},{$set:{ age: newAge}})
问题:查询和更新之间存在竞态窗口,并发场景下依然可能出现重复更新,无法真正实现幂等。
方案2:添加"op"字段作为状态锁
通过新增op字段标记操作状态(默认值none表示无操作),但无法保证操作原子性。
var doc = db.users.findOne({_id: "123", "op": "none"}) if (doc.op === "none") { db.users.updateOne({_id: "123"},{$set:{ op: 'pending'}}) db.users.updateOne({_id: "123", op: 'pending'},{$inc: {age: 1}, $set:{ op: 'none'}}) }
问题:两次updateOne操作并非原子执行,若中间出现中断(比如DocumentDB主节点不可用),op字段会停留在pending状态,导致后续操作无法正常执行。
方案3:添加"version"字段作为乐观锁
通过版本号控制更新,但多并发场景下性能较差。
while (true) { var doc = db.users.findOne({_id: "123"}) var curVersion = doc.version + 1 db.users.updateOne({_id: "123"},{$set:{ version: curVersion}}) var ret = db.users.updateOne({_id: "123", version: curVersion},{$inc: {age: 1}}) if (ret.modifiedCount == 1) { break; } }
问题:当多个$inc操作并发执行时,会频繁触发循环重试,导致整体性能下降。
实际场景
我们使用AWS DocumentDB,其主节点在维护期间会有数秒不可用,AWS支持建议实现该命令的幂等性,以配合重试策略处理更新失败问题,现寻求更优方案。
内容的提问来源于stack exchange,提问作者zangw
相关产品推荐
相关产品推荐

