交易

事务在 Redis 中的工作原理

Redis 事务允许执行一组命令 在一个步骤中,它们以命令为中心MULTI,EXEC,DISCARDWATCH. 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相反,将刷新事务队列并退出 交易。

以下示例递增键foobar自动。

> 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 返回错误。 如果在对命令进行排队时出现错误,则大多数客户端 将中止并丢弃事务。否则,如果客户端选择继续交易 这EXECcommand 将执行所有成功排队的命令,而不管以前的错误如何。

之后发生的错误 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 是OKcode 和 另一个是 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真的是关于?这是一个命令,它将 使EXECconditional:我们要求 Redis 执行 仅当没有WATCHed 键被修改。这包括 客户端所做的修改,如写入命令,以及 Redis 本身, 比如 expiration 或 eviction。如果键在WATCHed 和当EXEC,则整个交易将被中止 相反。

注意

  • 在 6.0.9 之前的 Redis 版本中,过期的 key 不会导致事务 中止。更多相关信息
  • 事务中的命令不会触发WATCHcondition 的 仅排队,直到EXEC已发送。

WATCH可以多次调用。简单地说,所有的WATCH调用将 具有 Effect 以监视从调用开始的更改,直到 当下EXEC被调用。您还可以将任意数量的密钥发送到 单WATCH叫。

什么时候EXEC被调用时,所有键都是UNWATCHed,无论 事务是否已中止。此外,当客户端连接为 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 事务执行作,也可以使用脚本执行作,并且 通常,脚本会更简单、更快速。

为本页评分
返回顶部 ↑