我们相信:世界是美好的,你是我也是。平行空间的世界里面,不同版本的生活也在继续...

类似于mysql的事务机制,redis也有事务机制。mysql中的select ... for update是一种悲观锁机制。而redis中的watch命令为代表的事务,是个乐观锁机制。本文的重要结论就是:redis的事务【不能回滚】!只能【中断或者跳过】执行!接下来就看看在redis事务中,watch(unwatch)、multiexec以及discard这几条重要组成命令是如何使用的吧!

苏南大叔:redis事务,watch、multi、exec以及discard命令的使用 - redis事务
redis事务,watch、multi、exec以及discard命令的使用(图6-1)

苏南大叔的“程序如此灵动”博客,记录苏南大叔的代码编程故事。本文测试环境:win10redis@5.0.14.1

前文回顾

redis下载地址:

redis官方没有提供windows版本的redis程序,官方建议在windows系统下的wsl虚拟机中使用redis。参考文章:

本文出于测试的目的,使用的是非官方绿色版的redis@5.0.14.1软件。如果不习惯命令行界面,还可以安装redisdesktop软件。参考文章:

启动进程

默认端口6379启动redis-server.exe,然后启动了两个redis-client.exe,用于模拟事务相关命令的使用环境。

  • 第一个redis-client.exe客户端,用于发起事务。
  • 第二个redis-client.exe客户端,用于破坏线程一的redis事务。

苏南大叔:redis事务,watch、multi、exec以及discard命令的使用 - 两个客户端
redis事务,watch、multi、exec以及discard命令的使用(图6-2)

redis 事务

事务其实就是一系列命令的组合,理论上来说是要不全部执行,要不全部不执行。而redis事务并不是完整概念上的事务,它没有回滚机制,会出现“一部分执行,某条执行失败,然后后续的命令再执行”的情况。也就是说:redis事务不具有原子性。或者说redis的事务实际上是个命令组合功能,并不是真正的事务。

redis事务命令,一共有以下几个命令所组成。

命令用途解释
multi组装事务类似于begin命令
exec执行事务类似于commit命令
discard取消事务注意不是回滚
watch监视【一些】key一旦这些key在事务执行(exec)之前被改变,则取消事务的执行。
unwatch取消所有被watch的变量效果没参数,取消所有变量

redis 事务之打包

先看个普通的redis事务功能:

set age 1
multi
set age 2
exec
get age

或者:

set age 1
multi
set age 2
discard
get age

multi开始一个事务,exec或者discard结束一个事务。或者由其它进程通过改变被watch的变量的方式,来被动discardredis事务不支持回滚!只支持取消!

苏南大叔:redis事务,watch、multi、exec以及discard命令的使用 - 普通的事务
redis事务,watch、multi、exec以及discard命令的使用(图6-3)

redis 事务之 watch

事务不一定要配合watch命令的。不执行watch命令的话,就是个普通的打包功能。watch是提供一个其它进程取消本线程事务的功能。redis在事务开始之前,可以先watch一个或几个变量。如果这些变量被其它进程改变了,就拒绝线程里对应事务全部命令的执行!

再解释一次:当前redis事务的执行,是基于被watch的某个或某几个key的不变性的。只有被watchkey不变,对应事务才能执行!否则就是被动的discard。而key是可以被其它线程改变的!看,就是这么的“舍小家为大家、大公无私、充满爱”的操作!

watch的变量,如果在对应的事务里面被修改,因为set key value是个队列,所以理论上来说,并不是真正的被修改。所以,一个redis事务在watch某个key的时候,又在内部修改对应key。并不是个悖论,是不发生冲突的。

例子一 watch

序号线程一线程二
0watch age
1multi
2set age 888
3set age 999
4exec
5get ageget age

最后的age取值是888,因为线程二通过设置修改age变量已经破坏了线程一的事务。(线程一的事务已经被隐式的discard了)

苏南大叔:redis事务,watch、multi、exec以及discard命令的使用 - 例子一
redis事务,watch、multi、exec以及discard命令的使用(图6-4)

例子二 watch

线程watch多个key也是可以的,任何一个key被其它线程修改的话,事务就会被隐式的discard

序号线程一线程二
0watch age location
1multi
2set location peking
3set age 999
3set location Seoul
4exec
5get ageget age
5get locationget location

这个例子里面age取值是888或者(nil)location取值是peking

例子三 unwatch

watchunwatch只对当前线程有效,不影响其它线程。

序号线程一线程二
0watch age
1unwatch
2multi
3set age 888
4set age 999
5exec
6get ageget age

苏南大叔:redis事务,watch、multi、exec以及discard命令的使用 - 例子三
redis事务,watch、multi、exec以及discard命令的使用(图6-5)

序号线程一线程二
0watch age
1multi
2unwatch
3set age 888
3set age 999
4exec
5get ageget age

苏南大叔:redis事务,watch、multi、exec以及discard命令的使用 - 例子四
redis事务,watch、multi、exec以及discard命令的使用(图6-6)

redis 事务之“防插队”

网上其它教程里面说redis事务最大的特点就是“防止插队”。苏南大叔对于这句话是这么理解的:

  • 事务内部保证顺序执行,事务内部防止插队。
  • 不同线程的两个事务之间,是没有先来后到的说法的。没有谁先multi谁就会被先执行的说法。谁先执行只取决于谁先exec

结语

相比较mysql事务的悲观锁,redis的事务是个乐观锁。就是说,本线程执行自己的线程,要是有其它人破坏了规则,那本线程就不执行了。很乐观很大方吧?
苏南大叔的更多redis经验文章,请点击下面的链接:

如果本文对您有帮助,或者节约了您的时间,欢迎打赏瓶饮料,建立下友谊关系。
本博客不欢迎:各种镜像采集行为。请尊重原创文章内容,转载请保留作者链接。

 【福利】 腾讯云最新爆款活动!1核2G云服务器首年50元!

 【源码】本文代码片段及相关软件,请点此获取更多信息

 【绝密】秘籍文章入口,仅传授于有缘之人   redis