面试进阶Redis(***)—— 不是“事务”的Redis事务

事务(Transaction)

一般性的“事务”最大特性————原子性,即封装在事务中的顺序操作要么全部执行,要么全部不执行,支持回滚(rollback)。而很遗憾,Redis的“事务”并不具有原子性。

 

Redis事务的实现原语

原语列表可以归结为如下[1]:

结合着Redis中文网关于Redis事务的介绍:http://www.redis.cn/topics/transactions.html,可以总结如下:

1)MULTI和EXEC分别为client事务的开始和“结束”

先看例子:

从multi开始,中间的所有Redis指令都会被放进队列中缓存,显示“QUEUED”即入队成功,一直到遇见exec,exec命令是将当前命令队列发送给服务端,让其执行,并接受服务端命令执行结果,这个结果的顺序和命令的顺序是一致的。可以看作不使用事务模式时,单个指令执行结果的综合。

2)DISCARD可以取消现有事务

DISCARD可以看作“消除了”从MULTI到DISCARD前一行的所有命令效果,看例子:

可以看到,DISCARD命令“消除了”之前自MULTI起的两次INCR命令,exec显示没有检测到MULTI命令,me的值也还是MULTI之前的值——1。

3)WATCH相当于给事务加锁

WATCH命令用于监视一个或若干个变量的值,如果WATCH之后到MULTI之前,这个/些变量变了,则事务直接取消。效果可以等效为在EXEC之前进行一次if判断,如果到MULTI之前监视的值没有变,则执行EXEC,若改变了则不执行了,事务取消。e.g.

注意:

① MULTI和EXEC之间(事务中)对监视key的改变不会影响到是否执行WATCH判断,这看似不合理,但是可以从Redis的client——server模型得到解释:MULTI到EXEC(事务中)之间的命令都只是放入了client事务的命令队列中,根本还没有送往server端执行——被监视的key根本没有受到事务中命令的影响。还是MULTI之前的值。

② WATCH指令可以同时监控多个值,格式:watch key1 key2 key3 ...

③ WATCH的监视效果默认是EXEC之后(不管事务是否成功发送)就失效了,如果想在EXEC之前就手动解除某个/些键值的监控,可以使用UNWATCH命令,但是注意,和①中的注意点相同:不要把UNWATCH指令放在事务之中(MULTI和EXEC之间)。

4)为什么说Redis的事务不是“事务”

之前的介绍中,Redis的事务也好像是那么会回事:事务的提交,处理。但是有一个关键点都还没有涉及:如果事务中的指令序列中间出现错误怎么办?e.g.

如图,“hget me me”命令肯定是错误的,因为me的value是String类型的(用的命令是set而不是hset/hmset),但是在事务中(MULTI和EXEC之间)这条指令是没法检查出错误的,理由就像之前说的那样,这些命令只会被装进事务的命令队列(返回QUEUED)中,并没有执行,在检查"hget me me"命令时,client并不知道内存中到底有没有hash类型的key为me,并且拥有me作为子键值之一的变量,client只会进行语法层面的检查:"hget me me"是符合hset命令格式的,予与exec!

联系到Java,这很类似于Java中的Runtime Exception,这是无法在编译期得知的。

回到话题,看下执行结果:

可以看到,exec“发送”完命令队列后,收到了3条server端返回的执行结果,分别对应于事务中三条指令的执行结果:竟然有2个OK,而通过之后的“get me”和“get you”也验证了这一点——"set me tom"和"set me jerry"的确执行成功了,而“hget me me”报了指令类型和操作数不匹配的错误。

很清晰:Redis的事务并不是真正的事务,只是命令队列而已,并没有保证原子性,不支持回滚

 

不过,可以通过一个小小的命令实现一个有限的原子性:

msetnx key1 value1 key2 value2

set key value(单key赋值) —— mset key1 value1 key2 value2 ...(多key赋值)

setnx key value —— set if Not eXist (如果key不存在则创建该key,并赋值value;若存在则不操作)

msetnx key1 value1 key2 value2 ... —— 若key1 key2 ... 都不存在,则创建,赋值;若有一个存在,命令取消。即原子性

 

 

 

参考资料:

[1] RUNOOB Redis事务介绍

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值