【问题标题】:redis-rb multi only increment if key setredis-rb multi 仅在键集时增加
【发布时间】:2015-07-22 07:29:51
【问题描述】:

我想在 redis 中存储一个计数。我只想在密钥存在时才增加计数。我究竟做错了什么? exists 正在返回 false 并且正在执行 incr。

key = "blah"
result = REDIS_DB.multi do
  exists = REDIS_DB.exists(key)
  REDIS_DB.incr(key) if exists
end

# result: [false, 1]

我是 redis 的新手。我刚读了the redis transactions doc。据我了解,multi中的命令应该一个接一个地执行?

Rails 4.0.2、Redis 3.0.1、redis-rb(Redis 的 Ruby 客户端库)

【问题讨论】:

    标签: ruby-on-rails ruby ruby-on-rails-4 redis race-condition


    【解决方案1】:

    这可能是我想要的:

    result = REDIS_DB.watch(key) do
      if REDIS_DB.exists(key)
        REDIS_DB.incr(key)
      else
        REDIS_DB.unwatch
      end
    end
    

    【讨论】:

      【解决方案2】:

      从 redis 2.6 开始支持 lua 脚本,它是 transactional by definition,因此也可以使用以下代码。

      redis_script = <<SCRIPT
          local exists = redis.call('exists', KEYS[1])
          if exists == 1 then
              return redis.call('incr', KEYS[1])
          end
      SCRIPT
      
      # returns incremented value if key exists otherwise nil
      REDIS_DB.eval(redis_script, ['testkey']) 
      

      阅读更多关于scripting and eval command的信息


      为什么incr 在你的实际代码中执行是因为

      multi 块中的每个 REDIS_DB 函数调用将返回一个 Redis::Future 对象而不是实际值,因为 redis-rb 将命令缓存在多块中。例如

      REDIS_DB.multi do 
          return_value = REDIS_DB.exists('tesstt')
          puts return_value.inspect
      end
      

      将打印

      <Redis::Future [:exists, "tesstt"]>
      

      现在,如果我们通过了您示例中的障碍

      exists = REDIS_DB.exists(key) # returns Redis::Future object
      REDIS_DB.incr(key) if exists  # evaluates to true as value of exists is an object
      

      那么result: [false, 1]从何而来。

      这是因为REDIS_DB.multi 在产生块(并缓存命令)后最终将其转换为 redis 查询并将其发送到 redis,redis 最终运行它并返回结果。所以你的代码实际上被转换成下面的查询。

      MULTI
      exists 'blah'
      incr 'blah'
      EXEC
      

      并在对 redis 的一次调用中一起提交,该调用返回 0 for exists(其中 redis-rb 转换为布尔值 false)和 1 for incr

      需要注意的是,这种行为是可以理解的,就像您将每个命令单独发送到redis然后redis本身会将MULTI之后的所有内容排队并在收到对exec的调用时对其进行处理

      【讨论】:

      • 这条评论是一个LIFESAVER! OP 的场景相对较小,但是这个特殊的 Redis multi gotcha 在我的案例中引起了疯狂的头痛。我有一个if ... else if ... else ... 块,实际上该块中的所有命令都在执行,对于我的一生,我不知道为什么。唯一的解释是 if, else-ifelse 条件没有被评估,我无法弄清楚为什么语言本身会像这样破坏!
      猜你喜欢
      • 2012-02-01
      • 1970-01-01
      • 2011-08-29
      • 2016-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多