在MySQL中,我们提到事务是一组不可分割的指令集合
。简单来说就是不能被打断,就像飞机起飞一样,中途不能有任何因素干扰,一旦被打断只能被迫中止回到原来的位置重新起飞。
在Redis中同样是这样。
1、基本操作
Redis中通过两个指令来控制事务的开启与提交。
multi #开启事务
#...各种操作
exec #执行事务
discard #取消事务,要注意一旦取消事务,相当于数据库回滚到事务开启时的状态。(等价于MySQL事务的回滚)
在事务执行的过程中,我们要注意两点:
- 如果发生语法型错误,事务会自动执行discard。
- 如果发生非语法错误,则出错的那条指令不执行,其他正常执行。
说个很扯淡的事儿,一般我们程序编写时会规避掉非语法错误,但是有万一嘛。因为发生非语法错误,你还要去想办法恢复,所以其实Redis的事务一般我们都不用O(∩_∩)O。(经典白学)
2、事务执行过程
过程看起来很复杂,但其实很简单:
事务的本质是将你要执行的操作按照顺序放在一个准备执行的队列里面,等到接收到exec指令,就按序执行。
就像学校里做核酸,你们一个班就是一个事务,接到通知说下来做核算(multi指令,事务开启),班里的每位同学就是一条要执行的指令,按学号排好队之后,辅导员说你们班开始做(exec指令,事务执行),你们班同学就按照拍好的顺序依此执行。如果:
- 在做的过程中发现检测试剂用错了(语法错误),那就所有人都回去吧(回到开始事务前的状态)。
- 有一个人没有带身份证和手机(非语法错误),那就他自己做不了,其他人不受影响。
3、锁
锁:个人认为在各种框架或语言里都是在高并发访问下保证数据安全有效的一种机制。
基本命令
watch key [key ...] #给一个或多个key上锁,执行事务过程中上锁的key一旦被其他客户端修改,事务回滚。
unwatch #将所有被上锁的数据解除
要注意的是,watch指令必须用在开启事务(multi指令)之前,不能在事务中使用。
分布式锁
在Redis中是没有提供有关分布式锁的实现指令的,不过我们可以通过相互约定
来实现共享锁。
我们利用Redis中key不能重复的原理来实现。当我们想要并发访问资源时,我们约定一个key(value值随意)为共享锁,在访问之前你要先拿到这把锁(也就是创建这个key)。
看上图,简单来说,就是你和你朋友现在逛街半天了都很累,在路上看到一辆哈罗单车。你俩都想要扫码使用。现在你俩约定,谁能摸到那个比较高的树叶(相当于图里的lock-tickets)谁就能用。一次只能一个人跳起来摸树叶(setnx新建lock-tickets,若lock-tickets存在则不能再次创建)。你摸到树叶(代表你setnx lock-tickets执行成功)。你就可以使用了。
你说要是没有拿到lock-tickets能不能使用?答案是能的。就像你朋友在你跳起来摸树叶的时候(相当于他不去获取lock-tickets,直接操作)偷偷扫码把车骑走了。你直接呆住。所以Redis中的共享锁是靠共同的约定来实现的。
死锁解决
如果现在两方约定好lock-tickets是共享锁,那么如果一方拿到之后,因为异常而一直不释放。岂不是造成死锁了。这让其他遵守约定的客户很难受啊。
这里我们可以将约定的锁
通过设置时效性
来解决。比如规定一个客户拿到锁之后,5个小时还不释放锁就将这个锁释放。
expire key seconds #设置s为单位的时效性
pexpire key milliseconds #设置ms为单位的时效性
时常设计规则: