watch()和multi()redis指令是如何工作的?

ewm0tg9j  于 2023-04-19  发布在  Redis
关注(0)|答案(1)|浏览(125)

我想了解redis-py版本3.5.3访问数据库Redis的指令multi()watch()的正确用法。Redis服务器版本为Redis server v=5.0.5
特别是,我编写并执行了以下代码,其中使用了指令watch,并在键keyWatch上使用:

r = redis.Redis()

def key_incr():
    print('keyWatch before incr = ' + r.get('keyWatch').decode("utf-8"))
    pipe = r.pipeline()
    pipe.watch('keyWatch')
    pipe.multi()
    pipe.incr('keyWatch')
    pipe.execute()
    print('keyWatch after incr = ' + r.get('keyWatch').decode("utf-8"))

key_incr()

前面的代码可以正确执行,其输出为(keyWatch的初始值为9):

keyWatch before incr = 9
keyWatch after incr = 10

如果我从代码中删除指令multi(),它将变为:

r = redis.Redis()

def key_incr():
    print('keyWatch before incr = ' + r.get('keyWatch').decode("utf-8"))
    pipe = r.pipeline()
    pipe.watch('keyWatch')
    # NOTE: here the multi() instruction is commented
    #pipe.multi()
    pipe.incr('keyWatch')
    pipe.execute()
    print('keyWatch after incr = ' + r.get('keyWatch').decode("utf-8"))

key_incr()

它的执行会引发以下异常:

raise WatchError("Watched variable changed.")
redis.exceptions.WatchError: Watched variable changed.

我的需要是避免其他客户端在事务内部修改密钥keyWatch,但为什么在我的示例代码中,只有当multi()指令不存在时才会引发WatchError异常?
谢谢

编辑

通过redis-cli monitor(在文章的其余部分中为MONITOR),我可以看到在执行前两段代码期间对服务器的请求。
对于multi()指令的情况,我有以下请求:

> redis-cli monitor
OK
1681733993.273545 [0 127.0.0.1:46342] "GET" "keyWatch"
1681733993.273790 [0 127.0.0.1:46342] "WATCH" "keyWatch"
1681733993.273934 [0 127.0.0.1:46342] "MULTI"
1681733993.273945 [0 127.0.0.1:46342] "INCRBY" "keyWatch" "1"
1681733993.273950 [0 127.0.0.1:46342] "EXEC"
1681733993.274279 [0 127.0.0.1:46342] "GET" "keyWatch"

对于没有multi()指令的情况,我有以下请求:

> redis-cli monitor
OK
1681737498.462228 [0 127.0.0.1:46368] "GET" "keyWatch"
1681737498.462500 [0 127.0.0.1:46368] "WATCH" "keyWatch"
1681737498.462663 [0 127.0.0.1:46368] "INCRBY" "keyWatch" "1"
1681737498.463072 [0 127.0.0.1:46368] "MULTI"
1681737498.463081 [0 127.0.0.1:46368] "EXEC"

在第二种情况下,也存在MULTI指令,但在它和EXEC之间没有任何请求。
keyWatch异常是由EXEC指令引发的,实际上MONITOR没有显示最后一个"GET" "keyWatch"请求(与第一个MONITOR日志比较,可以找到最后一个"GET" "keyWatch"请求)。
所有这些都表明异常是由执行以下命令引起的:
"INCRBY" "keyWatch" "1"在块MULTI/EXEC之外。
如果有人能证实这一点,并解释更好的行为是赞赏。
谢谢

j2cgzkjk

j2cgzkjk1#

WATCHMULTIEXEC被设计为一起工作。具体来说,对MULTI和EXEC的调用允许隔离地执行排队的命令。Redis称之为transaction
它是这样工作的:

MULTI                          <- start queueing commands
INCR someKey                   <- queue this command
SET someOtherKey someValue     <- queue this command
UNLINK someThirdKey            <- queue this command
EXEC                           <- execute all the queued commands

当这些命令正在排队时,其他命令可能会进入。这些其他命令可能会更改事务的一部分密钥,这可能是错误的。请输入WATCH。
WATCH用于监视这些键。如果在调用EXEC时它们已被更改,EXEC将返回错误。然后您需要运行代码重试事务(或者可能生成错误,具体取决于您的需要)。
如果它们没有被改变,那么EXEC执行所有排队的命令,生命继续。
它的工作原理是这样的:

WATCH someKey someOtherKey     <- watch these for changes
MULTI                          <- start queueing commands
INCR someKey                   <- queue this command
SET someOtherKey someValue     <- queue this command
UNLINK someThirdKey            <- queue this command
EXEC                           <- either error or execute queued commands

请注意,您不必像上面的例子所示的那样在事务中监视密钥。在这种情况下,我不在乎是否有人更改了我要删除的内容。
Redis中的事务并不是开发人员通常认为的那样。如果你想更深入地了解细节,Redis网站上有一个guide to transactions

相关问题