Redis中的交易和观察声明

时间:2012-05-25 07:54:26

标签: 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的值   注意它,我们的交易将失败。如果没有客户改变了   价值,该集合将起作用。我们可以在循环中执行此代码直到它   的工作原理。

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

1 个答案:

答案 0 :(得分:71)

这里有几个问题。

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命令的执行延迟到exec。所以客户端只会在内存中堆积GET和SET命令,并在最后一次执行它们并原子地执行它们。当然,尝试将电流分配给GET和增量的结果将很快发生。实际上,redis.get方法只返回字符串“QUEUED”以表示命令被延迟,并且增量将不起作用。

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

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确定值是一致的并且表示它是成功的。

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