0

我的帖子

个人中心

设置

  发新话题
相关命令:
  • DISCARD
  • EXEC
  • MULTI
  • UNWATCH
  • WATCH
事务MULTI,EXEC,DISCARD和WATCH是Redis事务的基础。它们允许在一个步骤中执行一组命令,并有两个重要的保证:
  • 事务中的所有命令都会被序列化并按顺序执行。在执行Redis事务的过程中,不会发生由另一个客户端发出的请求被服务的情况。这保证命令作为一个单独的隔离的操作被执行。
  • 无论是所有的命令都被处理还是没有命令被处理,Redis事务都是原子的。EXEC命令触发事务中所有命令的执行,因此如果在调用MULTI命令之前,客户端在事务上下文中失去与服务器的连接,则不执行任何操作,相反如果调用EXEC命令, 所有的操作都被执行。在使用仅追加文件时,Redis确保使用单个写入(2)系统调用将事务写入磁盘。但是,如果Redis服务器崩溃或被系统管理员以某种强制的方式杀死,可能只有部分操作被注册。Redis将在重新启动时检测到这种情况,并会在出现错误时退出。使用redis-check-aof工具可以删除部分事务修复仅追加文件,以便服务器可以重新启动。
从版本2.2开始,Redis允许为上述两项提供额外保证,采用与check-and-set(CAS)操作非常相似的乐观锁定形式。这将在本页后面记录。
用法使用MULTI命令进入Redis事务。该命令总是以OK回应。此时用户可以发出多个命令。Redis不会执行这些命令,而是将它们排队。EXEC被调用后,所有的命令都会被执行。
调用DISCARD而不是刷新事务队列并将退出事务。
以下示例以原子方式递增key 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 指示)。
  • 例如,因为我们针对具有错误值的key执行操作(例如,针对字符串值调用list操作),所以命令可能在调用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 3 abc +QUEUED LPOP a +QUEUED EXEC *2 +OK -ERR Operation against a key holding the wrong kind of valueEXEC返回two-element批量字符串回复,其中一个是OK代码,另一个是-ERR回复。客户端库需要找到一种明智的方式将错误提供给用户。
需要注意的是,即使命令失败,队列中的所有其他命令也会被处理–Redis不会停止命令的处理。
另一个例子,再次使用telnet协议使用Wire协议,可以显示语法错误是如何报告的:
MULTI +OK INCR a b c -ERR wrong number of arguments for ‘incr’ command这次由于语法错误,错误的INCR命令根本没有排队。
为什么Redis不支持回滚?如果您有关系数据库背景,Redis命令在事务处理期间可能会失败,但Redis将执行其余事务而不是回滚事务,这可能对您来说看起来很奇怪。
但是,对于这种行为有很好的意见:
  • 如果使用错误的语法调用Redis命令(并且在命令排队期间无法检测到问题),或者针对保存错误数据类型的key,则Redis命令可能会失败:这意味着实际上,失败的命令是编程错误的结果, 以及在开发过程中很可能检测到的一种错误,而不是在生产中。
  • Redis内部简化且速度更快,因为它不需要回滚功能。
反对Redis观点的一个观点是错误发生了,但是应该指出的是一般情况下,回滚并不能避免编程错误。例如,如果查询将key增加2而不是1,或增加错误的key,则回滚机制无法提供帮助。鉴于没有人能够挽救程序员的错误,并且Redis命令失败所需的错误类型不太可能进入生产环境,所以我们选择了不支持错误回滚的更简单快捷的方法。
放弃命令队列DISCARD可用于中止交易。在这种情况下,不执行任何命令并且连接状态恢复正常。
> SET foo 1 OK > MULTI OK > INCR foo QUEUED > DISCARD OK > GET foo “1”乐观锁定使用check-and-setWATCH用于为Redis事务提供check-and-set(CAS)行为。
监视key被监视以检测对它们的改变。如果在EXEC命令之前至少修改了一个监视的key,则整个事务中止,并且EXEC返回Null答复以通知事务失败。
例如,假设我们需要将key的值自动递增1(让我们假设Redis没有INCR)。
第一次尝试可能如下:
val = GET mykey val = val + 1 SET mykey $val只有当我们有一个客户端在给定时间内执行操作时,这才能可靠地工作。如果多个客户端尝试在大约同一时间递增key,则会出现竞争状况。例如,客户端A和B将读取旧值,例如10,这两个客户端的值将递增为11,最后将SET作为key的值。所以最终的值将是11而不是12。
感谢WATCH,我们能够很好地模拟这个问题:
WATCH mykey val = GET mykey val = val + 1 MULTI SET mykey $val EXEC使用上面的代码,如果存在竞争条件,并且另一个客户端在我们对WATCH的调用和我们对EXEC的调用之间的时间内修改了val的结果,则事务将失败。
我们只需要重复这次的操作,希望这次我们不会得到新的竞争。这种形式的锁定称为乐观锁定,是一种非常强大的锁定形式。在许多用例中,多个客户端将访问不同的key,因此碰撞不太可能发生 - 通常不需要重复该操作。
WATCH说明那么WATCH真的是什么? 这是一个使EXEC有条件的命令:只有在没有任何WATCHed key被修改的情况下,我们才会要求Redis执行事务。 (但是它们可能会被事务中的同一个客户端改变而不会中止它,更多的是这个)否则,事务根本不会进入。(请注意,如果您WATCH易失性key并且Redis在您WATCH该key后过期了该key,那么EXEC将继续工作。更多的是这个。)
WATCH可以多次调用。简单地说,所有的WATCH调用都将具有WATCH从调用开始发生变化的效果,直到EXEC被调用。您也可以将任意数量的key发送到单个WATCH调用。
当调用EXEC时,无论事务是否中止,所有key都是UNWATCHed。此外,当客户端连接关闭时,所有都会被UNWATCHed。
也可以使用UNWATCH命令(无参数)来刷新所有watch的key。
有时候,我们乐观地锁定了几个key,这很有用,因为可能我们需要执行一个事务来改变这些key,但是在读完了key的当前内容之后我们不想继续。发生这种情况时,我们只需调用UNWATCH,以便连接可以自由用于新事务。
使用WATCH来实现ZPOP举一个很好的例子来说明如何使用WATCH来创建新的原子操作,否则Redis不支持实现ZPOP,即以原子方式从排序集合中以较低分数弹出元素的命令。这是最简单的实现:
WATCH zset element = ZRANGE zset 0 0 MULTI ZREM zset element EXEC如果EXEC失败(即返回空回复),我们只需重复该操作。
Redis脚本和事务根据定义,Redis脚本是事务性的,因此您可以使用Redis事务执行的所有操作都可以通过脚本完成,通常脚本将更简单快捷。
这种重复是由于在Redis 2.6中引入了脚本,而事务早已存在。然而,我们不可能在短时间内取消对事务的支持,因为即使不采用Redis脚本编写,仍然可以避免竞争状况,尤其是因为Redis事务的实施复杂性最低,这在语义上似乎是恰当的。
然而,在不远的将来,我们将看到整个用户群只是使用脚本,这并非不可能。如果发生这种情况,我们可能会弃用并最终删除事务。


转载请并标注: “本文转载自 文/张强”




本帖最后由 七彩极 于 2018-5-2 17:57 编辑
提示: 作者被禁止或删除 内容自动屏蔽
‹‹ 上一贴:【教程经验】php最常用的函数总结表   |   下一贴:【教程经验】php进修资料:菜鸟变凤凰 ... ››
  发新话题
快速回复主题
关于我们 | 诚聘英才 | 联系我们 | 网站大事 | 友情链接 |意见反馈 | 网站地图
Copyright©2005-2018 51CTO.COM
本论坛言论纯属发布者个人意见,不代表51CTO网站立场!如有疑义,请与管理员联系:bbs@51cto.com