一尘不染

Redis中的交易和观察声明

redis

您能否以“ The Little Redis Book”中的示例为例向我解释一下:

使用上面的代码,我们将无法实现自己的incr命令,因为一旦exec被调用,它们就会一起执行。从代码中,我们不能:

redis.multi() 
current = redis.get('powerlevel') 
redis.set('powerlevel', current + 1) 
redis.exec()

这不是Redis事务的工作方式。但是,如果我们向powerlevel添加手表,则可以执行以下操作:

redis.watch('powerlevel') 
current = redis.get('powerlevel') 
redis.multi() 
redis.set('powerlevel', current + 1) 
redis.exec()

如果另一个客户在我们对其进行监视之后更改了powerlevel的值,则我们的交易将失败。如果没有客户端更改该值,则该设置将起作用。我们可以循环执行此代码,直到工作为止。

为什么我们不能在不能被其他命令中断的事务中执行增量?为什么我们需要进行迭代并等待直到没有人更改值 开始事务?


阅读 299

收藏
2020-06-20

共1个答案

一尘不染

这里有几个问题。

1)为什么我们不能在不能被其他命令中断的事务中执行增量?

首先请注意,Redis的“事务”与大多数人认为经典DBMS中的事务完全不同。

# Does not work
redis.multi() 
current = redis.get('powerlevel') 
redis.set('powerlevel', current + 1) 
redis.exec()

您需要了解在服务器端执行的操作(在Redis中),以及在客户端执行的操作(在脚本中)。在上面的代码中,GET和SET命令将在Redis一侧执行,但是对电流的分配和当前+1的计算应该在客户端执行。

为了保证原子性,MULTI /
EXEC块将Redis命令的执行延迟到执行为止。因此,客户端只会将GET和SET命令堆积在内存中,然后一次执行一次,最后自动执行。当然,将电流分配给GET和递增结果的尝试将早于之前发生。实际上,redis.get方法仅返回字符串“
QUEUED”以指示命令已延迟,并且增量将不起作用。

在MULTI /
EXEC块中,只能使用参数可以在块开始之前完全了解的命令。您可能需要阅读文档以获取更多信息。

2)为什么我们需要进行迭代并等待直到没有人更改值才开始交易?

这是并发乐观模式的一个示例。

如果我们不使用WATCH / MULTI / EXEC,则可能会出现竞争情况:

# Initial arbitrary value
powerlevel = 10
session A: GET powerlevel -> 10
session B: GET powerlevel -> 10
session A: current = 10 + 1
session B: current = 10 + 1
session A: SET powerlevel 11
session B: SET powerlevel 11
# In the end we have 11 instead of 12 -> wrong

现在,让我们添加一个WATCH / MULTI / EXEC块。使用WATCH子句,仅在值未更改的情况下才执行MULTI和EXEC之间的命令。

# Initial arbitrary value
powerlevel = 10
session A: WATCH powerlevel
session B: WATCH powerlevel
session A: GET powerlevel -> 10
session B: GET powerlevel -> 10
session A: current = 10 + 1
session B: current = 10 + 1
session A: MULTI
session B: MULTI
session A: SET powerlevel 11 -> QUEUED
session B: SET powerlevel 11 -> QUEUED
session A: EXEC -> success! powerlevel is now 11
session B: EXEC -> failure, because powerlevel has changed and was watched
# In the end, we have 11, and session B knows it has to attempt the transaction again
# Hopefully, it will work fine this time.

因此,您不必反复等待直到没有人更改该值,而是一次又一次尝试该操作,直到Redis确保值一致并发出成功信号。

在大多数情况下,如果“事务”足够快并且发生争用的可能性很低,则更新非常有效。现在,如果存在争用,则必须对某些“事务”执行一些额外的操作(由于迭代和重试)。但是数据将始终保持一致,并且不需要锁定。

2020-06-20