redis中的事务(八)

redis中也存在存在事务,可以和MySQL的事务可以对比一下,可能理解会更深刻些

MULTI/EXEC/DISCARD/WATCH这四个命令是我们实现事务的基石
详细解释每个命令的含义

  1. MULTI :用于启动事务,也就是在这条命令后的任何命令操作都存起来,不让它立马执行,也可以这么理解,延迟执行,等EXEC命令来让这些命令按顺序执行,
  2. EXEC: 执行一个在一个事务里的所有命令,并恢复非事务状态,所以multi和exec经常搭配使用
  3. DISCARD:回滚取消事务中的所有命令,说是回滚就是把事务中即将进行的命令取消掉
  4. WATCH: 在事务开启前监控某个或多个键,当在事务exec时这个键的值时如果这个值变化了(不是监控时的值了),那么此事务将不能进行下去,会回滚(此事务里的预执行的命令全部不执行)返回nil,当然不管该事务是执行成功还是回滚了,watch的监控的键就不再监控了

命令示例

1. 事务被正常执行

127.0.0.1:6379> multi   //在当前连接启动一个新的事务
OK
//执行事务中的第一条命令,从该命令的返回结果可以看出,该命令并没有立即执行,而是存于事务的命令队列。
127.0.0.1:6379> incr t1   //
QUEUED
//又执行一个新的命令,从结果可以看出,该命令也被存于事务的命令队列。
127.0.0.1:6379> incr t2
QUEUED
//执行事务命令队列中的所有命令,从结果可以看出,队列中命令的结果得到返回。
127.0.0.1:6379> exec
1) (integer) 1
2) (integer) 1

2. 事务中存在失败的命令:

127.0.0.1:6379> multi  //开启一个事务
OK
127.0.0.1:6379>  set a 3 //设置键a的值为String类型的3
QUEUED
//从键a所关联的值的头部弹出元素,由于该值是字符串类型,而lpop命令仅能用于List类型,因此在执行exec命令时,该命令将会失败
127.0.0.1:6379> lpop a 
QUEUED
127.0.0.1:6379> set a 4 //再次设置键a的值为字符串4
QUEUED
127.0.0.1:6379> get a //获取键a的值,以便确认该值是否被事务中的第二个set命令设置成功
QUEUED
//从结果中可以看出,事务中的第二条命令lpop执行失败,而其后的set和get命令均执行成功,也可以证明是按顺序执行的
127.0.0.1:6379> exec
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
3) OK
4) "4"

2.1 事务中存在语法错误的命令
这个到不用担心,因为在前面的练习中,自己输错语法肯定会报错

(error) ERR unknown command '错误的关键词'

3. 回滚事务:

127.0.0.1:6379> set t2 tt //添加数据
OK
127.0.0.1:6379> multi  //开启事务
OK
127.0.0.1:6379> set t2 ttnew  //再往t2的value添加ttnew,也就是覆盖
QUEUED
127.0.0.1:6379> discard  //自己主动取消事务,这样这个事务里的命令就不执行了,连接回归到非事务模式
OK
127.0.0.1:6379> get t2
"tt"

四、WATCH命令和基于CAS的乐观锁:(强调,不开始事务,那么watch是基本没有的)
乐观锁与悲观锁参考:http://www.cnblogs.com/qlqwjy/p/7798266.html ,不过我想乐观锁和悲观锁就算细节忘了,但是大体是什么,思路是什么应该不会忘记吧

127.0.0.1:6379> set mykey 5 //添加数据
OK
127.0.0.1:6379> watch mykey //watch监控mykey
OK
127.0.0.1:6379> incr mykey //自增长1,没问题,返回结果了,所以只要改变值不是在事务里,就不用担心watch的事
(integer) 6
127.0.0.1:6379> multi //开启事务
OK
127.0.0.1:6379> incr mykey //自增长 1
QUEUED
127.0.0.1:6379> exec //执行事务里的命令,返回的是nil,返回非事务模式
(nil) 
127.0.0.1:6379> get mykey //查看mykey
"6"

watch在事务中起到监控的功能,用于提供CAS功能,
假设我们通过WATCH命令在事务执行之前监控了多个key,倘若在watch之后有任何key的值发生了变化,EXEC命令执行的事务都将被放弃,同时返回nil
那肯定有它存在的道理(摘抄):
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
例子:客户端A和B都在同一时刻读取了mykey的原有值,假设该值为10,此后两个客户端又均将该值加一后set回Redis服务器,这样就会导致mykey的结果为11,而不是我们认为的12。为了解决类似的问题,我们需要借助WATCH命令的帮助

WATCH mykey
val = GET mykey
val = val + 1
MULTI
SET mykey $val
EXEC

和此前代码不同的是,新代码在获取mykey的值之前先通过WATCH命令监控了该键**,此后又将set命令包围在事务中,这样就可以有效的保证每个连接在执行EXEC之前,如果当前连接获取的mykey的值被其它连接的客户端修改,那么当前连接的EXEC命令将执行失败。这样调用者在判断返回值后就可以获悉val是否重新设置成功
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
自己做了个例子

127.0.0.1:6379> get mykey //查看mykey
"7"
127.0.0.1:6379> watch mykey //监控
OK
127.0.0.1:6379> multi  //开启事务
OK
127.0.0.1:6379> incr mykey //自增长1
QUEUED
127.0.0.1:6379> exec //执行事务,成功,和上面的做比较,这就是在事务执行之前监控的键是否被改变的区别
1) (integer) 8
127.0.0.1:6379> get mykey // 查看修改后的值
"8"

我在这还考虑了一种情况(老毛病,爱挑牛角尖)
当监控此键,但是我又输入把这个键给覆盖了,那再执行事务还能行吗?
正常的逻辑思路想到两条

  1. 我既然把它覆盖了,那么监控肯定没有了,那事务肯定能执行,但是结果相反,返回的是nil
  2. 虽然覆盖了,但是我监控还没取消,虽然有一模一样的新键值存在,但是监控watch保存了这个键被修改了的信息,当事务进行时有这个键的命令,从watch中得知被修改过了,就不再执行了(回滚),但是它为什么这么肯定我的事务要修改的是监控的键(或者出现一模一样的为什么不能执行)

大家可以看看我下面这个例子

127.0.0.1:6379> get mykey  //查看mykey的值
"8"
127.0.0.1:6379> watch mykey //监控mykey
OK
127.0.0.1:6379> set mykey 8 //覆盖mykey
OK
127.0.0.1:6379> get mykey //查看mykey的值
"8"
127.0.0.1:6379> multi //开启事务
OK
127.0.0.1:6379> incr mykey //mykey自增长 1
QUEUED
127.0.0.1:6379> exec //执行事务,返回nil
(nil)
127.0.0.1:6379> get mykey //查看mykey的值
"8"

我只能这么理解:你既然加watch了,说明下面事务里关于这个键的修改是监控时的数据,就算覆盖了也默认数据已经被修改,不能执行事务里的命令了(必须回滚)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值