Redis【有与无】【UR5】事务

本文基于Redis 6.0.9版本,前提至少 Redis 3.0或更高版本。

目录

相关命令

1.事务

1.1.用法

1.2.事务中的错误

1.3.为什么Redis不支持回滚?

1.4.丢弃命令队列

1.5.使用检查和设置进行乐观锁定

1.6.WATCH说明

1.7.使用WATCH实现ZPOP

1.8.Redis脚本和事务


相关命令

1.事务

MULTIEXECDISCARD 和 WATCH是Redis中事务的基础。 它们允许一步执行一组命令,并具有两个重要保证:

  • 事务中的所有命令都被序列化并顺序执行。 在Redis事务的执行过程中,永远不会发生另一个客户端发出的请求。 这样可以确保将命令作为单个隔离操作执行。
  • 所有命令都将被处理,或者不处理任何命令,因此Redis事务也是原子的。 EXEC命令触发事务中所有命令的执行,因此,如果客户端在调用EXEC命令之前,在事务上下文中,失去与服务器的连接,则不执行任何操作,而是调用EXEC命令, 所有操作均已执行。 使用仅附加文件Redis时,请确保使用单个write(2) syscall将事务写入磁盘。 但是,如果Redis服务器崩溃或被系统管理员以某种困难的方式杀死,则可能仅注册了部分操作。 Redis将在重新启动时检测到这种情况,并且将退出并显示错误。 使用redis-check-aof工具可以修复仅附加文件,该文件将删除部分事务,以便服务器可以重新启动。

从2.2版开始,Redis以乐观锁定的形式对上述两个方面提供额外保证,其方式与检查和设置(CAS)操作非常相似。 这将在此页面的稍后部分进行记录。

1.1.用法

使用MULTI命令输入Redis事务。 该命令始终以OK答复。 此时,用户可以发出多个命令。 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时,仅将调度的命令排定为执行。

1.2.事务中的错误

在事务期间,可能会遇到两种命令错误:

  • 命令可能无法排队,因此在调用EXEC之前可能存在错误。 例如,命令可能在语法上是错误的(错误的参数数量,错误的命令名称等),或者可能存在一些严重的情况,例如内存不足情况(如果服务器使用maxmemory配置为具有内存限制) 指示)。
  • 调用EXEC后,命令可能会失败,例如,因为我们对具有错误值的键执行了操作(例如,针对字符串值调用了列表操作)。

客户端通常通过检查排队命令的返回值来感知发生在EXEC调用之前的第一类错误:如果命令以QUEUED答复,则它已正确排队,否则Redis返回错误。 如果在排队命令时出现错误,大多数客户端将中止该事务并将其丢弃。

但是,从Redis 2.6.5开始,服务器将记住命令累积期间发生错误,并且将拒绝执行事务,并在EXEC期间返回错误并自动丢弃该事务。

在Redis 2.6.5之前,行为是仅在命令子集成功排队的情况下执行事务,以防客户端调用EXEC而不管先前的错误如何。 这种新行为使将事务与流水线混合起来变得更加简单,因此可以立即发送整个事务,稍后再读取所有答复。

相反,EXEC之后发生的错误不会以特殊方式处理:即使在事务期间某些命令失败,也会执行所有其他命令。

这在协议级别上更为清楚。 在以下示例中,即使语法正确,一条命令在执行时也会失败:

Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
MULTI
+OK
SET a abc
+QUEUED
LPOP a
+QUEUED
EXEC
*2
+OK
-ERR Operation against a key holding the wrong kind of value

EXEC返回两个元素的大容量字符串答复,其中一个是OK代码,另一个是-ERR答复。 由客户端库确定向用户提供错误的明智方法。

重要的是要注意,即使命令失败,队列中的所有其他命令也会得到处理– Redis不会停止命令的处理。

另一个示例,再次使用带有telnet的有线协议,显示了如何尽快报告语法错误:

MULTI
+OK
INCR a b c
-ERR wrong number of arguments for 'incr' command

这次由于语法错误,错误的INCR命令根本没有排队。

1.3.为什么Redis不支持回滚?

如果您具有关系数据库背景,则Redis命令在事务期间可能会失败,但Redis仍将执行事务的其余部分而不是回滚,这一事实对您来说可能很奇怪。

但是,对于此行为有好的意见:

仅当使用错误的语法(并且在命令排队期间无法检测到该问题)或针对包含错误数据类型的键调用Redis命令时,该命令才能失败:这实际上意味着失败的命令是编程错误的结果, 以及一种很可能在开发过程中而不是生产过程中发现的错误。

Redis在内部得到了简化和更快,因为它不需要回滚的能力。

反对Redis的观点是发生错误,但是应该注意的是,通常回滚并不能使您免于编程错误。 例如,如果查询将键增加2而不是1,或者将错误的键增加,则回滚机制将无济于事。 鉴于没有人可以挽救程序员免受错误的影响,并且Redis命令失败所需的错误类型不太可能进入生产环境,因此我们选择了不支持错误回滚的更简单,更快捷的方法。

1.4.丢弃命令队列

可以使用DISCARD来中止事务。 在这种情况下,不执行任何命令,连接状态恢复为正常。

> SET foo 1
OK
> MULTI
OK
> INCR foo
QUEUED
> DISCARD
OK
> GET foo
"1"

1.5.使用检查和设置进行乐观锁定

WATCH用于为Redis事务提供检查设置(CAS)行为。

监视已监视的键,以便检测对它们的更改。 如果在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

使用上面的代码,如果存在竞争条件,并且另一个客户端在调用WATCH 和调用EXEC之间的时间内修改了val的结果,则事务将失败。

我们只需要重复一次操作,希望这次不会有新的比赛。 这种锁定形式称为乐观锁定,是一种非常强大的锁定形式。 在许多用例中,多个客户端将访问不同的键,因此发生冲突的可能性很小-通常无需重复操作。

1.6.WATCH说明

那么 WATCH 到底是什么呢? 这是一条使EXEC有条件的命令:仅在未修改被监听的键的情况下,我们才要求Redis执行事务。 (但是,事务内部的同一客户端可能会更改它们而不会中止更改。有关更多信息。)否则,根本不会输入事务。 (请注意,如果您监视一个易失性键,并且在您监视它后Redis使键失效,EXEC仍然可以使用。)

可以多次调用WATCH 。 简单的说,所有WATCH 调用都会具有从调用开始一直到调用EXEC为止监视更改的效果。 您也可以将任意数量的键发送到单个WATCH 呼叫。

调用EXEC时,所有键都将被UNWATCHed,而不管事务是否被中止。 同样,当客户端连接关闭时,所有内容都将变为未监视状态。

也可以使用UNWATCH命令(不带参数)来刷新所有监视的键。

有时这是有用的,因为我们乐观地锁定了几个键,因为可能我们需要执行事务来更改那些键,但是在读取了键的当前内容之后,我们不想继续。 发生这种情况时,我们只需调用UNWATCH,这样连接就可以自由地用于新事务了。

1.7.使用WATCH实现ZPOP

一个很好的例子来说明如何使用WATCH 来创建新的原子操作,否则Redis不支持该操作是实现ZPOP(ZPOPMINZPOPMAX及其阻塞变体仅在版本5.0中添加),这是一个使用以下命令弹出元素的命令: 以原子方式从排序集中获得的较低分数。 这是最简单的实现:

WATCH zset
element = ZRANGE zset 0 0
MULTI
ZREM zset element
EXEC

如果EXEC失败(即返回Null答复),我们只需重复该操作即可。

1.8.Redis脚本和事务

Redis脚本在定义上是事务性的,因此您可以使用Redis事务进行任何操作,还可以使用脚本进行操作,通常该脚本会更简单,更快。

这种重复是由于以下事实:脚本是在Redis 2.6中引入的,而事务早已存在。 但是,我们不太可能在短期内取消对事务的支持,因为在语义上似乎是适当的,即使不诉诸Redis脚本,仍然有可能避免竞争状况,尤其是因为Redis事务的实现复杂性极低。

但是,在不久的将来,我们会看到整个用户群只是在使用脚本,这并非不可能。 如果发生这种情况,我们可能会弃用并最终删除事务。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

琴 韵

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值