
实际上,您的代码在翻转边界附近并不安全,因为您正在执行“获取”,(等待时间和思考),“设置”-无需检查“获取”中的条件是否仍然适用。如果服务器忙于项目1000,则有可能获得各种疯狂的输出,包括:
12...9991000 // when "get" returns 998, so you do an incr1001 // ditto1002 // ditto0 // when "get" returns 999 or above, so you do a set0 // ditto0 // ditto1
选项:
- 使用事务和约束API使逻辑并发安全
- 通过Lua脚本重写您的逻辑
scriptevaluate
现在,redis事务(按选项1)很困难。就我个人而言,我将使用“
2”-除了更易于编码和调试之外,这意味着您只有1次往返和 *** 作,而不是“ get,watch,get,multi,incr / set,exec
/丢弃”和“从头开始重试”循环来解决中止情况。如果您愿意,我可以尝试将其写为Lua-应该大约4行。
这是Lua的实现:
string key = ...for(int i = 0; i < 2000; i++) // just a test loop for me; you'd only do it once etc{ int result = (int) db.scriptevaluate(@"local result = redis.call('incr', KEYS[1])if result > 999 then result = 0 redis.call('set', KEYS[1], result)endreturn result", new RedisKey[] { key }); Console.WriteLine(result);}注意:如果需要参数化最大值,则可以使用:
if result > tonumber(ARGV[1]) then
和:
int result = (int)db.scriptevaluate(..., new RedisKey[] { key }, new RedisValue[] { max });(因此
ARGV[1]取的值
max)
有必要了解
eval/
evalsha(这就是
scriptevaluate调用) 没有与其他服务器请求竞争
,因此在
incr和之间没有任何变化
set。这意味着我们不需要复杂的
watch逻辑。
通过事务/约束API,这是相同的(我认为!):
static int IncrementAndLoopToZero(IDatabase db, RedisKey key, int max){ int result; bool success; do { RedisValue current = db.StringGet(key); var tran = db.CreateTransaction(); // assert hasn't changed - note this handles "not exists" correctly tran.AddCondition(Condition.StringEqual(key, current)); if(((int)current) > max) { result = 0; tran.StringSetAsync(key, result, flags: CommandFlags.FireAndForget); } else { result = ((int)current) + 1; tran.StringIncrementAsync(key, flags: CommandFlags.FireAndForget); } success = tran.Execute(); // if assertion fails, returns false and aborts } while (!success); // and if it aborts, we need to redo return result;}复杂吗?在 简单的成功案例 在这里则是:
GET {key} # get the current valueWATCH {key} # assertion stating that {key} should be guardedGET {key} # used by the assertion to check the valueMULTI # begin a blockINCR {key} # increment {key}EXEC # execute the block *if WATCH is happy*这是一项相当大的工作,涉及多路复用器上的流水线停顿。更复杂的情况(断言失败,监视失败,环绕)将具有稍微不同的输出,但应该可以工作。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)