众所周知,$inc
、$mul
、$add
运算符的update
命令在MongoDB中不是幂等的。我们想知道如何使用update
操作符幂等函数来生成$inc
命令。
给定一个文档users
的集合{"_id" : "123", "age" : 24, "name": "me"}
,我们希望通过db.users.update({_id: "123"}, {$inc: {age: 1}})
将age
增加1。显然,这个update
命令不是幂等的,因为当这个update
命令意外执行两次时,age
将增加2。
我们尝试过的。
$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})
但是,该解决方案未能保持update
命令的幂等性。
"op": "pending"
这样的字段作为锁,op
的默认值是none
,意味着这个文档上没有操作。当尝试增加age
时,将op
的值设置为“挂起”,然后在完成增加操作后将其重置为none
。var doc = db.users.findOne({_id:"123","op":“无”}) if (doc.op === "none") { db.users.updateOne({_id:"123"},{$set:{ op:‘未决’}) db.users.updateOne({_id:"123",op:‘未决’},{$inc:{age: 1},$set:{ op:‘none’}
此解决方案的问题是很难使上述操作保持原子化。。
"version": 1
这样的字段,作为一个乐观锁。而{ var doc = db.users.findOne({_id:"123"}) var curVersion = doc.version +1 db.users.updateOne({_id:"123"},{$set:{ version: curVersion}) var = db.users.updateOne({_id:"123",version: curVersion},{$inc:{age: 1}}) if (ret.modifiedCount == 1) { break;}}
这个解决方案的问题是,当存在多个simutinaously.操作inc
时,效果会很差。
是否有更好的解决办法使update
命令具有$inc
运算符幂等性?
这个需求是基于在我的示例中使用AWS documentDB的实际情况,在维护期间,documentDB的主节点无法在几秒钟内可用。对于AWS支持人员,我们应该在重试策略中使用带有update
操作符的$inc
命令来处理维护期间的update命令失败。
发布于 2022-08-24 12:26:23
您所描述的这个用例和边缘案例听起来非常奇怪,但下面是我如何处理这样一个问题的方法:
对于您想要更新的每个字段,您必须以某种方式跟踪它的最后一个更新--否则永远无法保证不会发生多个更新,最简单的方法是在文档中添加一个字段,但这也可以在一个单独的集合中跟踪。对于这个答案,我假设只有age
字段可以是$inc
'd。
因此,正如我前面提到的,我会添加age_update_time
,然后我就可以不担心地执行以下更新:
const aYearAgo = new Date(new Date().getTime() - 1000 * 60 * 60 * 24 * 365)
db.users.updateOne(
{
_id: "123",
$or: [
{ // either it's been a year since last update.
age_update_time: {$gt: aYearAgo}
},
{ // or an update is due.
age_update_time: {$exists: false}
}
]
},
{
$set: {
age_update_time: new Date()
},
$inc: {
age: 1
}
}
)
因为mongo的更新是原子的,所以保证每年只执行一次更新。
同样,正如前面提到的,这个usecase听起来很奇怪,我知道这可能是一个玩具的例子-但是如果您能够分享更多关于为什么在您的系统中存在这种行为的细节,我也许可以提供更好的建议。
https://stackoverflow.com/questions/73472843
复制相似问题