我想在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客户端库)
答案 0 :(得分:1)
这可能就是我想要的:
result = REDIS_DB.watch(key) do
if REDIS_DB.exists(key)
REDIS_DB.incr(key)
else
REDIS_DB.unwatch
end
end
答案 1 :(得分:1)
从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
执行实际代码是因为
REDIS_DB
块中的每个multi
函数调用将返回Redis::Future
对象而不是实际值,因为redis-rb
将命令缓存在多块中。 e.g。
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,最终运行它并返回结果。所以你的代码实际上转换成了以下查询。
MULTI
exists 'blah'
incr 'blah'
EXEC
并在一次调用redis时一起提交,返回0
exists
(redis-rb
转换为布尔false
)和1 incr
。
需要注意的是,这种行为是可以理解的,就好像你将每个命令单独发送到redis然后redis本身将在MULTI
之后对所有内容进行排队,并在收到对exec
的调用时对其进行处理