原文地址:https://redis.io/topics/transactions
本人最近在研究redis的过程中,看到国内对于redis的事务讨论激烈,但是却没有多少同志去看redis官方的事务文档,所以在此分享给大家以供参考。
事务
MULTI,EXEC,DISCARD和WATCH是redis的事务基础。它们允许在一个步骤中执行一组命令,具有两个重要保证:
用法
使用MULTI命令输入Redis事务。命令总是返回OK。此时用户可以发出多个命令。Redis将排队这些命令,而不是执行这些命令。一旦EXEC被调用,所有命令都被执行。
调用DISCARD将刷新事务队列并退出事务。
以下示例增加键foo和bar原子。
> MULTI OK > INCR foo QUEUED > INCR bar QUEUED > EXEC 1) (integer) 1 2) (integer) 1
当Redis连接处于MULTI请求的上下文中时,所有命令将使用字符串QUEUED(从Redis协议的角度发送为状态回复)进行回复。排队的命令只是调EXEC时才执行。
事务内的错误
在事务过程中,可能会遇到两种命令错误:
在Redis 2.6.5之前,行为是执行事务,只要命令队列成功排队,以防客户端调用EXEC,而不管以前的错误。新的行为使事务与流水线混合变得更加简单,以便可以一次发送整个事务,稍后再次阅读所有的回复。
在 EXEC 之后发生的错误不是以特殊方式处理:即使某些命令在事务中失败,所有其他命令也将被执行。这在协议级别上更为明确。在以下示例中,即使语法正确,一个命令也将失败:
Trying 127.0.0.1... Connected to localhost. Escape character is '^]'. MULTI +OK SET a 3 abc +QUEUED LPOP a +QUEUED EXEC *2 +OK -ERR Operation against a key holding the wrong kind of value
重要的是要注意,即使命令失败,队列中的所有其他命令都将被处理 - Redis 不会停止处理命令。
另一个例子,再次使用有线协议telnet,显示如何以ASAP方式报告语法错误:
MULTI +OK INCR a b c -ERR wrong number of arguments for 'incr' command
如果您有关系数据库背景,Redis命令在事务中可能会失败,但是Redis仍将执行其余的事务而不是回滚,对您来说可能会很奇怪。
然而对于这种行为有好的观点:
可以使用DISCARD来中止事务。在这种情况下,不执行命令,连接状态恢复正常。
> SET foo 1 OK > MULTI OK > INCR foo QUEUED > DISCARD OK > GET foo "1"
WATCH用于向Redis事务提供支票(CAS)行为。
WATCH监视ed键以检测对它们的变化。如果在EXEC命令之前修改了至少一个被监视的密钥,则整个事务将中止,并且EXEC返回一个Null应答以通知该事务失败。
例如,假设我们需要将键的值原子地增加1(假设Redis没有INCR)。
第一次尝试可能如下:
val = GET mykey val = val + 1 SET mykey $val
感谢WATCH,我们能够很好地建模问题:
WATCH mykey val = GET mykey val = val + 1 MULTI SET mykey $val EXEC
我们只需要重复这个操作,希望这次我们不会得到一个新的比赛。这种锁定形式称为乐观锁定,是一种非常强大的锁定形式。在许多用例中,多个客户端将访问不同的密钥,因此不太可能发生冲突 - 通常无需重复操作。
那么WATCH究竟是什么呢?这是一个使EXEC有条件的命令:我们要求Redis只有在没有WATCHed键被修改的情况下才执行该事务。(但是它们可能会被事务中的同一个客户端所改变,而不会中止它)更多关于此事情)否则,根本不输入事务。(请注意,如果您WATCH挥发性键,你的Redis到期后的关键WATCH编吧,EXEC仍然可以工作。)
WATCH可以多次调用。所有WATCH电话都将具有观看从呼叫开始的更改的效果,直到调用EXEC。您也可以将任意数量的密钥发送到单个WATCH呼叫。
当EXEC被调用时,所有的密钥都被UNWATCH编辑,不管事务是否中止。当客户端连接关闭时,一切都会被修改UNWATCH。也可以使用UNWATCH命令(无参数)来刷新所有被监视的键。有时,这是有用的,因为我们乐观地锁定几个键,因为可能我们需要执行一个事务来更改这些键,但是在阅读了我们不想继续的键的当前内容之后。当这种情况发生时,我们只需打电话给 UNWATCH,以便连接可以自由地用于新的事务。
说明如何使用WATCH来创建Redis不支持的新原子操作的一个很好的例子是实现ZPOP,这是一个以原子方式从排序集弹出具有较低分数的元素的命令。这是最简单的实现:
如果EXEC失败(即返回Null答复),我们只需重复该操作。
WATCH zset element = ZRANGE zset 0 0 MULTI ZREM zset element EXEC
一个Redis的脚本是定义事务性的,所以一切都可以用Redis的事务做的,你也可以做一个脚本,通常脚本会更简单,更快速。
这种重复是由于在Redis 2.6中引入了脚本,而事务早已存在。然而,我们不太可能在短时间内消除对事务的支持,因为它似乎在语义上是适时的,即使不使用Redis脚本,仍然可以避免竞争条件,特别是因为Redis事务的实现复杂度最小。
然而,在不久的将来我们将看到整个用户群只是使用脚本是不可能的。如果发生这种情况,我们可能会弃用并最终删除事务。