3

我有一个 redis 服务器,我想实现一个原子(或伪原子)方法,它将执行以下操作(注意:我有一个与 redis 服务器有多个会话的系统):

  1. 如果某个键K存在,则获取它的值
  2. 否则,使用某个函数F(生成salts)生成的随机值调用SETNX 函数
  3. 向 redis 询问刚刚由当前会话生成的密钥K的值(或由另一个会话“同时”生成 - 在当前会话生成它之前的片刻)

我不想使用函数F预先生成(在检查值是否存在之前)值,并在键不存在时使用它的原因是:

  1. 我不想毫无理由地调用F(它可能会导致密集的 CPU 行为(
  2. 我想避免下一个有问题的情况: T1:会话 1 生成随机值VAL1 T2:会话 1 询问密钥K是否存在并得到“假” T3:会话 2 生成随机值VAL2 T4:会话 2 询问密钥K是否存在并得到“假” T5:会话 2使用值VAL2调用SETNX并从现在开始使用VAL2 T6:会话 1使用值VAL1调用SETNX并从现在开始使用VAL1,其中键K的实际值为VAL2

我创建的 python 伪代码是:

    import redis
    r = redis.StrictRedis(host='localhost', port=6379, db=0)
    ''' gets the value of key K if exists (r.get(K) if r.exists(K)), 
    otherwise gets the value of key K if calling SETNX function returned TRUE 
    (else r.get(K) if r.setnx(K,F())), meaning this the sent value is really the value,
    otherwise, get the value of key K, that was generated by another session a         
    short moment ago (else r.get(K))
    The last one is redundant and I can write "**r.setnx(K,F()) or True**" to get the 
    latest value instead, but the syntax requires an "else" clause at the end '''
    r.get(K) if r.exists(K) else r.get(K) if r.setnx(K,F()) else r.get(K)

还有其他解决方案吗?

4

1 回答 1

5

是的,您可以为此使用WATCH 。这是redis-py的修改示例:

def atomic_get_set(some_key):
    with r.pipeline() as pipe:
        try:
            # put a WATCH on the key that holds our sequence value
            pipe.watch(some_key)
            # after WATCHing, the pipeline is put into immediate execution
            # mode until we tell it to start buffering commands again.
            # this allows us to get the current value of our sequence
            if pipe.exists(some_key):
                return pipe.get(some_key)
            # now we can put the pipeline back into buffered mode with MULTI
            pipe.multi()
            pipe.set(some_key, F())
            pipe.get(some_key)
            # and finally, execute the pipeline (the set and get commands)
            return pipe.execute()[-1]
            # if a WatchError wasn't raised during execution, everything
            # we just did happened atomically.
        except WatchError:
            # another client must have changed some_key between
            # the time we started WATCHing it and the pipeline's execution.
            # Let's just get the value they changed it to.
            return pipe.get(some_key)
于 2014-07-16T13:08:28.617 回答