Redis事务
Posted 大坑水滴
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis事务相关的知识,希望对你有一定的参考价值。
Redis事务以MULTI开始,中间添加多种命令,这些命令不会立即执行,而是被放入到一个队列中,当执行EXEC时,队列中的所有命令被依次执行。
当命令放在MULTI中,但还未执行EXEC时,每个命令返回值为QUEUED,Redis事务将多个命令使用MULTI包括起来,调用EXEC一起执行,减少与客户端之间通信往返次数,提升执行多个命令时的性能
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set key 1 QUEUED 127.0.0.1:6379> LPUSH list a QUEUED 127.0.0.1:6379> EXEC 1) OK 2) (integer) 1 127.0.0.1:6379> keys * 1) "list" 2) "key" 127.0.0.1:6379>
从2.6.5版本开始,在执行EXEC之前,redis命令在加入队列时,如果出现错误(一般为语法错误),则执行EXEC时,该事务不会被执行,并自动丢弃
127.0.0.1:6379> FLUSHALL OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> SET flag 1 QUEUED 127.0.0.1:6379> LPUSH list a QUEUED 127.0.0.1:6379> LPUSH list (error) ERR wrong number of arguments for ‘lpush‘ command 127.0.0.1:6379> EXEC (error) EXECABORT Transaction discarded because of previous errors. 127.0.0.1:6379> keys * (empty list or set) ## 正确的脚本也未执行,当前事务被丢弃 127.0.0.1:6379>
在2.6.5版本之前,执行MULTI之后,EXEC之前发生错误时,当执行EXEC命令,redis忽略掉错误命令,执行正确的命令。
在EXEC执行之后发生错误,其他正确的命令会被执行,redis不会回滚
127.0.0.1:6379> FLUSHALL OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set mykey 1 QUEUED 127.0.0.1:6379> LPUSH mykey 12 QUEUED 127.0.0.1:6379> EXEC 1) OK 2) (error) WRONGTYPE Operation against a key holding the wrong kind of value 127.0.0.1:6379> keys * 1) "mykey" ## 正确命令被执行,错误的命令被丢弃 127.0.0.1:6379>
Redis不支持事务回滚,官网给出了两点理由
1、redis命令只有两个错误会出现,一个为错误的语法结构,一个为对数据类型使用错误的方法处理,如上例中使用LPUSH操作字符串类型的mykey。这两个问题在开发环境自测的时候就能够发现。
2、redis内部的结构简单,而且速度更快,因为它不需要回滚功能。
DISCARD命令可用于终止当前事务并丢弃,也即是清空队列中的命令。该命令必须应用在MULTI命令中
127.0.0.1:6379> FLUSHALL OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set key v QUEUED 127.0.0.1:6379> set mykey 2 QUEUED 127.0.0.1:6379> LPUSH list aa QUEUED 127.0.0.1:6379> DISCARD OK 127.0.0.1:6379> EXEC (error) ERR EXEC without MULTI ## 已经不再事务范围之内,也即是当前事务已经在执行DISCARD的时候结束 127.0.0.1:6379>
以上为redis简单的事务操作,但以上存在数据安全问题
假设小王暑假打工赚了一百块钱,存了起来,但由于小王所在地的一些方面的原因,钱放进去,但还未执行EXEC
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> SET money 100 QUEUED 127.0.0.1:6379>
此时小王妈妈去银行给小王汇过来1000元,并且营业员服务周到,各种硬件完备,很快就办完了业务
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set money 1000 QUEUED 127.0.0.1:6379> EXEC 1) OK 127.0.0.1:6379> get money "1000" 127.0.0.1:6379>
此时小王的汇款也结束了,(执行了EXEC)
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> SET money 100 QUEUED 127.0.0.1:6379> EXEC 1) OK 127.0.0.1:6379> get money "100" 127.0.0.1:6379>
平白无故丢了一千大洋,还得上十年学,十个暑假才能赚回来这些钱,很心塞。小王的问题也有办法解决,那就是在redis 2.2及以后版本之后引入的WATCH命令
小王再次存钱卡主
127.0.0.1:6379> FLUSHALL OK 127.0.0.1:6379> WATCH money ### 监控money OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set money 100 QUEUED
小王的妈妈依然是去银行转钱给小王,效率依然杠杠的
127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set money 1000 QUEUED 127.0.0.1:6379> EXEC 1) OK 127.0.0.1:6379> get money "1000" 127.0.0.1:6379>
小王存的钱开始被写入
127.0.0.1:6379> FLUSHALL OK 127.0.0.1:6379> WATCH money OK 127.0.0.1:6379> MULTI OK 127.0.0.1:6379> set money 100 QUEUED 127.0.0.1:6379> EXEC (nil) 127.0.0.1:6379> get money "1000" 127.0.0.1:6379>
小王存钱没有成功,查看了下余额,发现已经有1000大洋了,那么这一百块就可以去挥霍了,吃饱喝足,网吧包夜走起。。。
WATCH命令是一种乐观锁的实现,基于CAS(check and set,在java的JUC下atomic中,一些Atomic开头的类,也使用了CAS原理,只不过在java中被称为compare and set,但大致意思是一样的)。watch用于监控某个key是否发生了改变,如果在一个事务中,某个key在设置参数之后,在执行exec之前,其他客户端修改了该key,则该事务将返回null。
1、watch必须与multi一起使用,才会发生作用,并且其必须在multi之前执行
2、watch监控的元素在当前事务提交之前发生变化(另一个事务执行了exec),则无论当前事务中有多少命令,全部失败。
3、watch监控的元素在当前事务提交之前,被放入到另外一个事务的队列中,但并未执行exec,则当前事务可正常提交
4、watch监控的元素,被另外一个客户端在非MULTI包括的命令中修改,则无论当前事务中有多少命令,也将全部失败
以上是关于Redis事务的主要内容,如果未能解决你的问题,请参考以下文章