如何从Ruby中的MULTI块中读取Redis?

我在MULTI事务中封装了一组复杂的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") => "bar" 

显然我希望最后一个返回值是'baz' – 我怎么能实现这个目标?

你不能,因为所有命令(包括get)实际上都是在exec时执行的。 在这种情况下,get命令仅返回future对象,而不是实际值。

有两种方法可以实现此类交易。

使用WATCH子句

watch子句用于防止并发更新。 如果在watch和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或更高版本,可以在服务器上执行Lua脚本。 整个脚本的执行是primefaces的。 它可以很容易地在Ruby中实现:

 cmd = < 

这通常比使用WATCH子句简单得多。

正如塞尔吉奥在评论中指出的那样,你不能选择像Redis那样执行MULTI块。 请参阅有关交易的文档 :

要么处理所有命令,要么不处理任何命令。

但是,您可以使用WATCH来使用check-and-set(伪代码)实现乐观锁定:

 SET foo bar WATCH foo $foo = GET foo MULTI if $foo == 'bar' SET foo baz EXEC GET foo 

使用WATCH ,仅当观察的密钥未被更改时才会执行事务。 如果更改了监视键, EXEC将失败,您可以再试一次。

另一种可能性是使用脚本function,但这仅在2.6候选版本中可用。