将NodeJS与MongoDB + Mongoose一起使用。
首先,我知道异步非阻塞代码的优点。所以我会处理回调。但最后我遇到了以下问题。
假设我有一个可以被用户随时调用的功能。并且有可能,一个超级“闪电般速度”的用户几乎同时呼叫它两次。
function do_something_with_user(user_id){
User.findOne({_id:user_id}).exec(function(err,user){ // FIND QUERY
// Do a lot of different stuff with user
// I just cannot update user with a single query
// I might need here to execute any other MongoDB queries
// So this code is a set of queries-callbacks
user.save() // SAVE QUERY
})
}
当然它执行如下:查找查询,查找查询,保存查询,保存查询
这完全打破了应用程序的逻辑(应该查找查询,保存查询,查找查询,保存查询)。所以我决定通过“锁定”特定用户的整个函数来防止异步行为(因此在函数代码中仍然是异步的。)
var lock_function_for_user = {}
function do_something_with_user(user_id){
if(!lock_function_for_user[user_id]){
lock_function_for_user[user_id] = true
User.findOne({_id:user_id}).exec(function(err,user){
// Same code as above
user.save(function(){
lock_function_for_user[user_id] = false
})
})
} else {
setTimeout(function(){
do_something_with_user(user_id)
},100) // assuming that average function execution time is 100ms in average
}
}
所以,我的问题是:这是一个好习惯,好黑客还是坏黑客?如果这是一个糟糕的黑客,请提供任何其他解决方案。特别是,我怀疑这个解决方案在扩展和启动多个NodeJS流程时都能正常工作。
答案 0 :(得分:2)
这是一种非常糟糕的做法,你永远不应该使用计时器来控制代码流。
这里的问题叫做原子性。如果您需要执行find-save,find-save,那么您需要以某种方式打包这些操作(事务)。这取决于您使用的软件。在redis中,您有multi和exec命令。在mongodb中你有findAndModify()。另一种解决方案是使用索引。当您尝试将相同的字段保存两次时,会出现错误。在mongoose:
中的schemaType中使用属性“index:true”和“unique:true”var schema = mongoose.Schema ({
myField: { type: String, index: true, unique: true, required: true },
});
这就是您所需要的:Mongodb - Isolate sequence of operations - Perform Two Phase Commits。但是考虑到如果你需要进行大量的交易,mongodb可能不是最好的选择。
答案 1 :(得分:0)
您不想浪费RAM,因此请更换
lock_function_for_user[user_id] = false
与
delete lock_function_for_user[user_id]
除此之外:你可以just be optimistic and retry if a conflict happens。只需省略锁定并确保数据库在出现错误时注意到(并在这种情况下重试)。当然,哪种方式更好取决于这种冲突发生的频率。