这里有几个问题。
1)为什么我们不能在不能被其他命令中断的事务中执行增量?
首先请注意,Redis的“事务”与大多数人认为经典DBMS中的事务完全不同。
# Does not workredis.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 valuepowerlevel = 10session A: GET powerlevel -> 10session B: GET powerlevel -> 10session A: current = 10 + 1session B: current = 10 + 1session A: SET powerlevel 11session B: SET powerlevel 11# In the end we have 11 instead of 12 -> wrong
现在,让我们添加一个WATCH / MULTI / EXEC块。使用WATCH子句,仅在值未更改的情况下才执行MULTI和EXEC之间的命令。
# Initial arbitrary valuepowerlevel = 10session A: WATCH powerlevelsession B: WATCH powerlevelsession A: GET powerlevel -> 10session B: GET powerlevel -> 10session A: current = 10 + 1session B: current = 10 + 1session A: MULTIsession B: MULTIsession A: SET powerlevel 11 -> QUEUEDsession B: SET powerlevel 11 -> QUEUEDsession A: EXEC -> success! powerlevel is now 11session 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确保值一致并发出成功信号。
在大多数情况下,如果“事务”足够快并且发生争用的可能性很低,则更新非常有效。现在,如果存在争用,则必须对某些“事务”执行一些额外的操作(由于迭代和重试)。但是数据将始终保持一致,并且不需要锁定。



