黑龙江省建设安全协会网站建站系统推荐
1、事务概要
Redis事务是一个单独的隔离操作:
- 事务中的所有命令都会序列化、按顺序地执行。
- 事务在执行的过程中,不会被其他客户端发送来的命令请求所打断。
Redis事务的主要作用
串联多个命令,防止别的命令插队。
事务的3个命令
- Multi
- Exec
- discard
从输入Multi命令开始,输入的命令都会一次进入命令队列中,但不会执行,直到输入Exec后,Redis会将之前的命令队列中的命令依次执行(组队过程)。
组队的过程中,可以通过discard命令来放弃组队。
其实,这里类似于"二阶事务(2PC)",但是本质是不同的!:
- 输入Multi命令进入队列排队 类似于2PC中的事务准备阶段
- 输入Exec命令 类似于2PC中的事务提交阶段
- discard命令类似于事务的回滚阶段
如图:
案例1
开启事务到执行队列:
案例2
开启事务到放弃组队
以上是事务正常执行的案例,下面演示出问题的案例:
1>当在组队过程中的命令出现错误,直接执行队列,那么执行阶段会提示"transaction discard"
理解为:“队伍中,一人坑,全队输”
如下:
2>当组队过程中的命令正常,执行队列时,其中的命令存在异常。
结果:哪个命令存在错误,哪个命令就不会执行,并提示错误信息
总结
Redis事务其实就是把一个客户端的多个命令串联起来,防止其他客户端插队。并分为命令排队阶段和执行队列阶段,提供队列中的回滚操作(discard),当排队过程中,出现错误,那么队列不会执行;当组队成功后,执行队列时,某个命令出现错误,那么该命令会执行失败,并提示错误信息,其他正常的命令正常执行。
2、事务冲突
这里用下面案例来描述:
一个淘宝账号被3个人同时登录,且同一时间购买不同的商品,账号只有1w的余额:
甲:购买8000元的商品
乙:购买5000元的商品
丙:购买1000元的商品
假设购买时,redis是按甲、乙、丙的顺序依次执行"减少余额"的命令,最终,余额出现负数
解决方案有2种:
悲观锁
悲观锁(Pessimistic Lock):顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所有每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block,直到它拿到锁。
传统的关系型数据库里边就会用到很多这种锁机制,比如行锁、表锁等,读锁、写锁等,都是在做操作之前先上锁。
如下:
步骤详解:
- 甲获取到锁,在甲没有释放锁之前,别人没办法去操作余额
- 当甲购买完释放锁(余额-8000=2000)
- 锁被释放后,乙才能获取锁
- 这时乙去购买商品,判断此时"2000-5000"为负数,固不执行购买操作,余额不变
- 乙释放锁。
乐观锁
乐观锁(Optimistic Lock)顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下再此期间别人有没有去更新这个数据,可以使用版本号等机制实现。
乐观锁适用于多读的应用类型,这样可以提高吞吐量。Redis就是利用这种check-and-set机制实现事务的。
步骤详解:
- 甲乙丙在购买商品时,给数据(余额)添加一个版本号v1.0
- 甲乙先进行购买,都获取到了余额(1w)及版本号
- 对数据进行更新前,会判断当前版本号是否与拿到的版本号相同,相同则对数据进行操作,不同则终止命令。会更新这个版本号
- 假设此时甲获取到余额版本为v1.0,与数据的版本号相同,则购买商品,余额减去8000,并把余额版本号更新为v1.1
- 乙比甲稍微慢点,拿到的版本号也是v1.0,但是在处理数据时,发现当前的版本号为v1.1,与拿到的版本不一致,固终止命令
- 丙后面获取到余额版本号为v1.1,处理数据时,与当前版本号v1.1一致,再余额-1000,更新版本号为v1.2
3、watch命令
语法:
watch key1 key2 key3 ...
在执行multi之前,先执行watch key1 ...,可以监视一个(或多个)key,如果在事务执行之前,这个(或这些)key被其他命令所改动,那么事务将被打断,返回nil(空)
案例: