从 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的调用时对其进行处理