Redis事务系列之一Redis事务详解

Posted 程序编织梦想

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis事务系列之一Redis事务详解相关的知识,希望对你有一定的参考价值。

一、前言

本章是redis事务系列知识第一章,redis事务系列主要讲解以下内容:

redis 事务
redis乐观锁讲解
redis乐观锁实现秒杀
我们一步一步来,本章主要讲解事务。

二、事务

2.1事务阐述

讲redis事务之前,我们先来看看事务的定义。

事务:一个不可分割的工作单位,事务中包括的操作要么都做,要么都不做。

但redis的事务不并不是完整意义上的事务,我们称之为弱事务,为什么这么说呢?

redis中并没有回滚这种机制,也就是说,在一个事务中,中间一条命令执行失败,并不会导致前面已经执行的指令回滚。也不会造成后续的指令不做。我会在下面画图验证不支持回滚。

2.2 事务命令讲解

事务的命令一共就五条,为了方便大家记忆,我们先记住下面三条命令,:

1.开启事务:MULTI

2.执行事务:EXEC

3.取消事务:DISCARD

就像mysql中用begin开启事务、用commit结束事务一样。redis中是用multi开启事务,用exec执行命令。如果在exec之前你不想执行事务了,可以用discard取消当前事务。下面我们举例说明:

开启事务和执行事务的例子

> multi    //开启事务
> set s1 aaa 
> set s2 bbb
> exec    //执行事务
> get s1  // 获取s1的值
"aaa"
> get s2  // 获取s2的值
"bbb"

开启事务和取消事务的例子

> multi   //开启事务
> set s3 222  
> discard //结束事务
OK
> exec    //因为事务已经在上面取消了,所以在这里执行事务会报错。
(error) ERR EXEC without MULTI
> get s3  //事务被取消了,所以这里s3根本就没有值
(nil) 

2.3 事务为什么不支持回滚

讲解“事务为什么不支持回滚”之前我们先来汇总一下redis事务执行异常的几种情况,然后再总结事务为什么不支持回滚。

1.语法错误导致事务执行异常,该事务取消
在开启事务后,修改h1值为11111,h2值为2222,但h2语法错误,最终导致事务提交失败,h1、h2保留原值。

> set h1 11
> set h2 22
> multi      //开启事务
> set h1 11111
> sets h2 2222   // 语法错误,下面报错
(error) ERR unknown command `sets`, with args beginning with: `h2`, `2222`,
127.0.0.1:6379> exec   //执行事务,因为前面有语法错误,所以此事务取消
(error) EXECABORT Transaction discarded because of previous errors.
> get h1  // h1和h2的值并没有在事务中改变
"11"
> get h2
"22"

2.运行时错误(Redis类型错误)导致事务异常

在开启事务后,修改s1值为1111111,s2值为2222222,但将s2的类型作为List,在运行时检测类型错误,最终导致事务提交失败,此时事务并没有回滚,而是跳过错误命令继续执行, 结果s1值改变、s2保留原值。

> set s1 11
> set s2 22
> multi
> set s1 1111111
> lpush s2 2222222 //此处类型错误,s2的类型是字符传,但我们把S2的类型作为List提交
> exec  //提交执行事务报错。
1) OK
2) (error) WRONGTYPE Operation against a key holding the wrong kind of value
> get s1  //s1的值修改成功
"1111111"
> get s2   //s1的值修改失败
"22"

总结:为什么Redis不支持事务回滚?
以上两个例子总结出,多数事务失败是由语法错误或者数据结构类型错误导致的,语法错误说明在命令入队前就进行检测的,而类型错误是在执行时检测的,Redis为提升性能而采用这种简单的事务,这是不同于关系型数据库的,特别要注意区分。

2.4 watch和unwatch命令讲解

2.4.1 watch命令

上面我们讲到redis是的事务是不支持回滚的,但是我们一定要让它回滚怎么办呢?这就需要用的watch命令了。

watch使用要注意:watch在mutil命令之前使用.

watch的作用是:监控一个值是否发生变化,如果没发生改变,它会执行事务队列中的命令,提交事务;如果发生变化,将不会执行事务中的任何命令,同时事务回滚。最后无论是否回滚,Redis都会取消执行事务前的WATCH命令。

这么说不太好理解,我们画图表示一下:

下面我们用代码验证一下,场景如下:

在事务开始前用WATCH监控a1,之后修改a1为c1111,MULTI开始事务,修改a2为c2222,执行EXEC,返回nil,说明事务回滚;查看下a2的值都没有被事务中的命令所改变。

代码如下:

//先设置2个值,我们用监控a1,然后用a2判断是否发生的回滚。
127.0.0.1:6379> set a1 1111
127.0.0.1:6379> set a2 2222
127.0.0.1:6379> watch a1 //监听a1
127.0.0.1:6379> set a1 c1111 // a1的值在监控后发生了改变。
127.0.0.1:6379> multi //开始事务
127.0.0.1:6379> set a2 c2222  //设置a2的值
127.0.0.1:6379> exec  //执行事务,发生错误,事务回滚
(nil)
127.0.0.1:6379> get a2 //a2的值并没有被更改,依旧是2222
"2222"

2.4.2 unwatch命令

unwatch命令是取消监控,这里就不过多介绍了,下面是代码:

> set k1 1111
> set k2 2222
> WATCH k1
> set k1 11 //改变k1的值
> UNWATCH //取消监控
> MULTI    //开启事务
> set k1 12
> set k2 22
> exec    //执行事务成功了
1) OK
2) OK
> get k1
"12"
> get k2
"22"

三、结尾

好了,就讲到这里吧,这一章虽然知识点不多,但是小编为了更加通俗地讲出来也想了2、3天。
码字不易,希望大家关注一下小编,感谢!

另外,大家帮忙关注一下我微信公众号,感谢!关注后我有上百套学习资料和视频课程赠送。

以上是关于Redis事务系列之一Redis事务详解的主要内容,如果未能解决你的问题,请参考以下文章

Redis事务系列之二Redis实现乐观锁

Redis事务系列之二Redis实现乐观锁

Redis事务系列之三Redis乐观锁实现秒杀

Redis事务系列之三Redis乐观锁实现秒杀

redis入门到精通系列:redis的事务详解

redis入门到精通系列:redis的事务详解