您能否以“ 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的值,则我们的交易将失败。如果没有客户端更改该值,则该设置将起作用。我们可以循环执行此代码,直到工作为止。
为什么我们不能在不能被其他命令中断的事务中执行增量?为什么我们需要进行迭代并等待直到没有人更改值 才 开始事务?
这里有几个问题。
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确保值一致并发出成功信号。
在大多数情况下,如果“事务”足够快并且发生争用的可能性很低,则更新非常有效。现在,如果存在争用,则必须对某些“事务”执行一些额外的操作(由于迭代和重试)。但是数据将始终保持一致,并且不需要锁定。