交易
事务在 Redis 中的工作原理
Redis 事务允许执行一组命令
在一个步骤中,它们以命令为中心MULTI
,EXEC
,DISCARD
和WATCH
.
Redis 事务提供了两个重要的保证:
-
事务中的所有命令都被序列化和执行 顺序。其他客户端发送的请求永远不会 在 Redis 事务执行过程中服务。 这保证了命令作为单个 隔离作。
-
这
EXEC
命令 触发事务中所有命令的执行,因此 如果客户端在 transaction 在调用EXEC
不执行任何作 执行,如果执行EXEC
命令时,所有 作。使用仅追加文件时,Redis 确保 使用单个 write(2) syscall 将事务写入磁盘。 但是,如果 Redis 服务器崩溃或被系统管理员杀死 在某种困难的方式中,可能只有部分作 已注册。Redis 将在重新启动时检测到此情况,并退出并显示错误。 使用redis-check-aof
工具中,可以修复 append only 文件,该文件将删除部分事务,以便 服务器可以再次启动。
从版本 2.2 开始,Redis 允许对 以上两个,以 Optimistic Lock 的形式,以一种非常类似于 check-and-set (CAS)作。 这将在本页的后面记录。
用法
Redis 事务是使用MULTI
命令。命令
始终回复OK
.此时用户可以发出多个
命令。Redis 不会执行这些命令,而是将 Redis 排队
他们。所有命令都执行一次EXEC
被调用。
叫DISCARD
相反,将刷新事务队列并退出
交易。
以下示例递增键foo
和bar
自动。
> MULTI
OK
> INCR foo
QUEUED
> INCR bar
QUEUED
> EXEC
1) (integer) 1
2) (integer) 1
从上面的会话中可以清楚地看出,EXEC
返回一个
回复数组,其中每个元素都是单个命令的回复
在事务中,命令的发出顺序相同。
当 Redis 连接位于MULTI
请求
所有命令都将以字符串QUEUED
(作为状态回复发送
从 Redis 协议的角度来看)。排队命令是
简单地安排在执行时间EXEC
被调用。
事务中的错误
在事务期间,可能会遇到两种命令错误:
- 命令可能排队失败,因此之前可能会出现错误
EXEC
被调用。 例如,命令可能在语法上是错误的(参数数量错误, 错误的命令名称,...),或者可能存在一些严重情况,例如 内存条件(如果服务器配置为具有内存限制,则使用maxmemory
指令)。 - 命令可能会在
EXEC
被调用,例如,因为我们执行了 针对值错误的键的作(如对 String 值调用 List作)。
从 Redis 2.6.5 开始,服务器会在命令堆积过程中检测到错误。
然后,它将拒绝执行事务,并在EXEC
,放弃事务。
Redis < 2.6.5 的注意事项:在 Redis 2.6.5 之前,客户端需要检测在
EXEC
通过检查 queued 命令的返回值:如果命令回复 QUEUED,则为 正确排队,否则 Redis 返回错误。 如果在对命令进行排队时出现错误,则大多数客户端 将中止并丢弃事务。否则,如果客户端选择继续交易 这EXEC
command 将执行所有成功排队的命令,而不管以前的错误如何。
之后发生的错误 EXEC
而是不以特殊方式处理:
即使在事务期间某些命令失败,所有其他命令也会被执行。
这在协议级别上更为明显。在下面的示例中,一个 命令在执行时也会失败,即使语法正确:
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
MULTI
+OK
SET a abc
+QUEUED
LPOP a
+QUEUED
EXEC
*2
+OK
-WRONGTYPE Operation against a key holding the wrong kind of value
EXEC
返回的双元素 bulk string reply,其中 one 是OK
code 和
另一个是 Error 回复。由客户端库来查找
将错误提供给用户的合理方式。
请务必注意,即使命令失败,队列中的所有其他命令也会被处理 – Redis 不会停止 命令的处理。
另一个示例,再次使用 wire 协议telnet
,显示了
语法错误会尽快报告:
MULTI
+OK
INCR a b c
-ERR wrong number of arguments for 'incr' command
这次由于语法错误坏了INCR
命令未排队
完全。
回滚呢?
Redis 不支持事务回滚,因为支持回滚 将对 Redis 的简单性和性能产生重大影响。
丢弃命令队列
DISCARD
可用于中止事务。在这种情况下,没有
执行命令,并将连接的状态恢复到
正常。
> SET foo 1
OK
> MULTI
OK
> INCR foo
QUEUED
> DISCARD
OK
> GET foo
"1"
使用 check-and-set 的乐观锁定
WATCH
用于向 Redis 提供检查和设置 (CAS) 行为
交易。
WATCH
监控 ed 键以检测针对它们的更改。如果
至少有一个 watched key 被修改,然后EXEC
命令、
整个事务中止,并且EXEC
返回一个 Null 回复以通知
交易失败。
例如,假设我们需要以原子方式递增值
的键被 1(假设 Redis 没有INCR
).
第一次尝试可能如下:
val = GET mykey
val = val + 1
SET mykey $val
只有当我们有一个客户端执行
作。如果多个客户端尝试递增密钥
大约在同一时间,将存在争用条件。例如
客户端 A 和 B 将读取旧值,例如 10。该值将
由两个客户端递增到 11,最后SET
作为值
的密钥。因此,最终值将为 11 而不是 12。
由于WATCH
我们能够很好地对问题进行建模:
WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC
使用上面的代码,如果存在争用条件和另一个客户端
修改val
在我们呼吁之间的时间里WATCH
和
我们的呼吁EXEC
,事务将失败。
我们只需要重复该作,希望这次我们不会得到 新种族。这种形式的锁定称为乐观锁定。 在许多用例中,多个客户端将访问不同的密钥, 因此,不太可能发生碰撞 – 通常无需重复该作。
WATCH 解释
那么什么是WATCH
真的是关于?这是一个命令,它将
使EXEC
conditional:我们要求 Redis 执行
仅当没有WATCH
ed 键被修改。这包括
客户端所做的修改,如写入命令,以及 Redis 本身,
比如 expiration 或 eviction。如果键在WATCH
ed 和当EXEC
,则整个交易将被中止
相反。
注意
WATCH
可以多次调用。简单地说,所有的WATCH
调用将
具有 Effect 以监视从调用开始的更改,直到
当下EXEC
被调用。您还可以将任意数量的密钥发送到
单WATCH
叫。
什么时候EXEC
被调用时,所有键都是UNWATCH
ed,无论
事务是否已中止。此外,当客户端连接为
closed,一切都会得到UNWATCH
编辑。
也可以使用UNWATCH
命令(不带参数)
以刷新所有监视的键。有时这很有用,因为我们
乐观地锁定几个 key,因为可能需要执行
transaction 来更改这些键,但在读取当前内容
我们不想继续的键。发生这种情况时,我们只需调用UNWATCH
这样 Connection 就可以自由地用于新的
交易。
使用 WATCH 实现 ZPOP
一个很好的例子来说明如何WATCH
可用于创建新的
否则 Redis 不支持的原子作是实现 ZPOP
(ZPOPMIN
,ZPOPMAX
并且它们的 blocking 变体仅被添加
在 5.0 版本中),这是一个命令,该命令会弹出具有较低
score 的 SCORE 中。这是最简单的
实现:
WATCH zset
element = ZRANGE zset 0 0
MULTI
ZREM zset element
EXEC
如果EXEC
失败(即返回 Null 回复),我们只需重复该作。
Redis 脚本和事务
对于 redis 中的事务类作,需要考虑的其他内容是事务性的 redis 脚本。万事 您可以使用 Redis 事务执行作,也可以使用脚本执行作,并且 通常,脚本会更简单、更快速。