带锁定的Redis分布式增量

带锁定的Redis分布式增量,第1张

带锁定的Redis分布式增量

实际上,您的代码在翻转边界附近并不安全,因为您正在执行“获取”,(等待时间和思考),“设置”-无需检查“获取”中的条件是否仍然适用。如果服务器忙于项目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

选项:

  1. 使用事务和约束API使逻辑并发安全
  2. 通过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*

这是一项相当大的工作,涉及多路复用器上的流水线停顿。更复杂的情况(断言失败,监视失败,环绕)将具有稍微不同的输出,但应该可以工作。



欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/zaji/4954689.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-11-13
下一篇2022-11-13

发表评论

登录后才能评论

评论列表(0条)

    保存