如何在Ruby中的多块中读取Redis?
我在多事务中封装了一组复杂的Redis命令,但事务中的逻辑取决于Redis中已有的值。但事务中的所有读取似乎都返回如何在Ruby中的多块中读取Redis?,ruby,transactions,redis,Ruby,Transactions,Redis,我在多事务中封装了一组复杂的Redis命令,但事务中的逻辑取决于Redis中已有的值。但事务中的所有读取似乎都返回nil 下面的示例演示了问题: [Dev]> $redis.set("foo", "bar") => "OK" [Dev]> $redis.multi{ $redis.set("foo", "baz") if $redis.get("foo") == "bar" } => ["bar"] [Dev]> $redis.get("foo") => "b
nil
下面的示例演示了问题:
[Dev]> $redis.set("foo", "bar")
=> "OK"
[Dev]> $redis.multi{ $redis.set("foo", "baz") if $redis.get("foo") == "bar" }
=> ["bar"]
[Dev]> $redis.get("foo")
=> "bar"
显然,我希望最后一个返回值是
'baz'
——我如何实现这一点?正如Sergio在他的评论中所说,您不能选择性地执行Redis中那样的多
块。见:
要么处理所有命令,要么不处理任何命令
但是,您可以使用check和set实现乐观锁定(伪代码):
使用WATCH
,仅当监视的密钥未更改时,才会执行事务。如果更改了watch键,则执行将失败,您可以重试
另一种可能是使用脚本功能,但这仅在2.6候选版本中可用。您不能,因为所有命令(包括get)实际上都是在执行时执行的。在这种情况下,get命令只返回未来的对象,而不是实际值 实现这种事务有两种方法 使用WATCH子句 watch子句用于防止并发更新。如果变量的值在watch和multi子句之间更新,则不会应用multi块中的命令。由客户机在另一时间尝试该事务
loop do
$redis.watch "foo"
val = $redis.get("foo")
if val == "bar" then
res = $redis.multi do |r|
r.set("foo", "baz")
end
break if res
else
$redis.unwatch "foo"
break
end
end
这里的脚本有点复杂,因为块的内容可以是空的,所以没有简单的方法知道事务是否已被取消,或者它是否根本没有发生。除非交易被取消,否则在所有情况下,当多块返回结果时通常更容易
使用Lua服务器端脚本
使用Redis 2.6或更高版本。整个脚本的执行是原子的。它可以在Ruby中轻松实现:
cmd = <<EOF
if redis.call('get',KEYS[1]) == ARGV[1] then
redis.call('set',KEYS[1],ARGV[2] )
end
EOF
$redis.eval cmd, 1, "foo", "bar", "baz"
cmd=恐怕你不能这么做。使用Lua而不是WATCH命令,或者相反,是否有性能上的折衷?Lua更快,因为:1-它避免了客户端和redis服务器之间的多次往返;2-您不必通过循环来掩盖潜在的故障,那么Lua的缺点是它在脚本期间会阻止redis接收额外的命令,对吗?是的,任何Lua脚本都是以原子方式执行的-在处理Lua脚本时,不能处理任何其他命令。
cmd = <<EOF
if redis.call('get',KEYS[1]) == ARGV[1] then
redis.call('set',KEYS[1],ARGV[2] )
end
EOF
$redis.eval cmd, 1, "foo", "bar", "baz"