如何使用python在redis中实现原子获取或设置和获取密钥?

时间:2014-07-16 08:30:35

标签: python python-2.7 redis redis-py

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

  1. 如果某个键 K 存在,则获取其值
  2. 否则,请使用某个函数 F 生成的随机值调用SETNX function(生成salts
  3. 向redis询问当前会话(或其他会话"同时" - 当前会话生成之前的短暂时间)生成的密钥 K 的值
  4. 我不想预先生成(在检查值是否存在之前)具有函数 F 的值的原因,并且如果密钥不存在则使用它是:

    1. 我不想在没有任何理由的情况下致电 F (这可能会导致密集的CPU行为(
    2. 我想避免下一个有问题的情况: T1:会话1生成随机值 VAL1 T2:会话1询问密钥 K 是否存在并且" False" T3:会话2生成随机值 VAL2 T4:会话2询问密钥 K 是否存在并且" False" T5:会话2使用值 VAL2 调用SETNX并从现在开始使用 VAL2 T6:会话1使用值 VAL1 调用SETNX并从现在开始使用 VAL1 ,其中键 K 的实际值为 VAL2
    3. 我创建的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)
      

      还有其他解决方案吗?

1 个答案:

答案 0 :(得分:4)

是的,您可以使用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)