Redis事务详解,Redis事务不支持回滚吗?

Redis事务提供2个重要保证

MULTI, EXEC, DISCARD 和WATCH命令是Redis事务操作的基础 。他们可以让Redis在一个步骤里执行一组命令,且能做到如下2个重要保证:

事务中的所有命令都是序列化且都是按顺序执行的。在一个客户端执行Redis事务的过程中,不会接收其他任何客户端对它发出的请求。这保证了这些命令是作为一个单独的独立操作执行的。所有的命令要么都被一起处理,要么全都没有被处理,所以Redis事务是原子的。EXEC会命令触发事务中所有命令的执行。当正在使用AOF时,Redis会使用一个简单的write(2)系统级调用来确保把事务写入到磁盘。但是,如果Redis服务崩溃,或者被系统管理员以某种强制的方式杀死,那么可能只有部分命令被写入到磁盘。Redis在重新启动时会检测这种情况,并报错,然后退出。使用 redis-check-aof工具可以检查AOF,并移除那不完整的事务,使服务可以再次启动

从 2.2 开始, Redis 可以用另外一个形式(乐观锁)来确保以上2点,后面讲详细讨论.

用法

使用 MULTI 命令进入事务模式.这个命令只会返回OK。这个时候,用户就可以发出多个要一起执行的命令了。 Redis暂时不会执行这些命令,而是把它们放进队列。 当 EXEC 被调用时,所有的命令才会被一次性执行.

相反的,如果调用了 DISCARD 命令,则会清除事务队列中的所有命令,然后退出事务.

下面一个示例,原子性的递增keys foo 和 bar。

Redis使用事务原子性的递增变量

从上面可以看出, EXEC 返回一个数组, 包含事务中每个命令的执行结果,且响应的顺序和命令发出时的顺序一样。

当一个Redis链接正处于一个MULTI 请求的上下文时, 所有命令的响应结果都是字符串QUEUED。当调用EXEC时,会一次性执行这些命令。

错误

 

在Redis事物中,可能会发生2种错误:

命令可能排队失败。如,命令的语法可能是错误 (错误的参数个数,错误的命令名字 ...),或一些重要的环境问题,如,内存不足。当EXEC 调用后 ,一些命令可能执行失败,如,在一个字符串上进行了列表命令的操作。

第一种错误发生在 EXEC 命令之前,是很容易发现的,可以通过检查命令的返回值:如果返回值是 QUEUED ,那么它就是成功, 不然就是失败,Redis会返回一个错误。如果在命令的队列期间发出一个错误,通常的做法是,终止事务。

从 Redis 2.6.5 开始, 在命令排队期间发生错误,Redis会拒绝执行 EXEC,并返回一个错误,然后自动放弃这个事务。

在 Redis 2.6.5 之前,EXEC调用后,会执行排队成功的命令,忽略失败的命令。

这新的规则让事务和管道的一起使用变得简单, 这样整个事务都可以一起发送,然后一次性获取所有的回复,可以节省来往交互操作。

执行 EXEC 后:所有的命令都会被执行,甚至是那些错误的命令。

在下面的例子中,事务被执行,其中有一个命令返回失败:

成功执行事务,但有一个命令错误

EXEC 返回2个字符串 ,其中一个是OK ,另一个是-ERR, 由客户端来决定如何处理这个结果。

要注意的是 就算有命令失败,队列中的其他命令也会被执行。

当发生语法错误的时候,会尽快的报告错误:

语法错误会立即返回错误

以上是 INCR 命令的语法错误,它不会被放进命令队列。

为什么Redis不支持回滚

Redis命令在事务中可能会执行失败,但是Redis事务不会回滚,而是继续会执行余下的命令。如果您有一个关系型数据库的知识,这对您来说可能会感到奇怪,因为关系型数据在这种情况下都是会回滚的。

Redis这样做,主要是因为:

只有当发生语法错误(这个问题在命令队列时无法检测到)了,Redis命令才会执行失败, 或对keys赋予了一个类型错误的数据:这意味着这些都是程序性错误,这类错误在开发的过程中就能够发现并解决掉,几乎不会出现在生产环境。由于不需要回滚,这使得Redis内部更加简单,而且运行速度更快。

放弃事务

DISCARD可以中止一个事务。这种情况下,不会执行任何命令,然后客户端的链接的状态会还原成正常状态。

Redis放弃事务

乐观锁(check-and-set)

WATCH 命令为事务提供一个check-and-set (CAS) 行为。

WATCH 可以用来监听事务中的队列中的命令,在EXEC之前,一旦发现有一个命令被修改了的 , 那么整个事务就会终止, EXEC返回一个 Null ,提示用户事务失败了。

例如, 我们要自动的递增一个key (假设Redis还没有实现 INCR命令).

那代码可能如下:

如果在同一时间,只有一个客户端在执行上面的操作,那么上面的操作是可靠的。但如果同时有多个用户执行上面的操作的话,那结果就是不可靠的了

我们可以使用 WATCH 来很好的解决这个问题:

使用以上的代码,当有另一个用户在我们调用WATCH 和 EXEC之间,修改了mykey的值val,那么这个事务就会失败。这种形式的锁称为乐观锁,是一种非常强大的锁。

WATCH 说明

WATCH 到底是什么意思呢? 这个命令使得 EXEC 命令的执行必须满足一个条件:如果被WATCH的 keys 没有一个被更改(但它们可以在事务中被修改),则执行事务;不然,就不会执行这个事务。(注意,如果你 WATCH了一个有生命周期的key,并且这个key过期了, EXEC 依然会执行)

WATCH 可以被多次调用。所有的WATCH 调用都会在 EXEC 调用之前起作用。WATCH可以接收任意多的key 。

当 EXEC 被调用后, 所有的keys都将UNWATCH,不管这个事务会不会终止。同样,当一个客户端链接关闭后, 一切都将UNWATCH。

可以使用UNWATCH (没有参数)命令来刷新所有被WATCH的keys。有时会这样操作,我们乐观地锁定了几个keys,因为可能我们需要执行一个事务来修改这些keys,但是在读取了keys的当前内容之后,我们不想继续处理了。那么这个时候,我们就可以调用UNWATCH。

用 WATCH 实现ZPOP

这是一个不错的示例,它阐述了如何使用WATCH 来产生新的原子操作,这个没有被Redis支持的ZPOP,这命令以原子的方式从一个有序集合中弹出较低的分数的值:

如果 EXEC 失败 (如,返回 Null ) 我们只需要重新执行这个操作即可。

参考文献:

https://baijiahao.baidu.com/s?id=1613631210471699441&wfr=spider&for=pc

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值