分布式锁
什么是分布式锁?
分布式锁是控制分布式系统之间同步访问共享资源的一种方式。
为什么要使用分布式锁?
为了保证共享资源的数据一致性。
什么场景下使用分布式锁?
数据重要且要保证一致性
如何实现分布式锁?
主要介绍使用redis来实现分布式锁
redis事务
redis事务介绍:
1.redis事务可以一次执行多个命令,本质是一组命令的集合。
2.一个事务中的所有命令都会序列化,按顺序串行化的执行而不会被其他命令插入
**作用:**一个队列中,一次性、顺序性、排他性的执行一系列命令
multi指令的使用
1. 下面指令演示了一个完整的事物过程,所有指令在exec前不执行,而是缓存在服务器的一个事物队列中
2. 服务器一旦收到exec指令才开始执行事物队列,执行完毕后一次性返回所有结果
3. 因为redis是单线程的,所以不必担心自己在执行队列是被打断,可以保证这样的“原子性”
注:redis事物在遇到指令失败后,后面的指令会继续执行
1 2 3 4 5 6 |
|
注:mysql的rollback与redis的discard的区别
mysql回滚为sql全部成功才执行,一条sql失败则全部失败,执行rollback后所有语句造成的影响消失
redis的discard只是结束本次事务,正确命令造成的影响仍然还在.
1)redis如果在一个事务中的命令出现错误,那么所有的命令都不会执行;
2)redis如果在一个事务中出现运行错误,那么正确的命令会被执行。
watch 指令作用
实质:WATCH 只会在数据被其他客户端抢先修改了的情况下通知执行命令的这个客户端(通过 WatchError 异常)但不会阻止其他客户端对数据的修改
1. watch其实就是redis提供的一种乐观锁,可以解决并发修改问题
2. watch会在事物开始前盯住一个或多个关键变量,当服务器收到exec指令要顺序执行缓存中的事物队列时,redis会检查关键变量自watch后是否被修改
3. WATCH 只会在数据被其他客户端抢先修改了的情况下通知执行命令的这个客户端(通过 WatchError 异常)但不会阻止其他客户端对数据的修改
watch+multi实现乐观锁
setnx指令(redis的分布式锁)
1、分布式锁
分布式锁本质是占一个坑,当别的进程也要来占坑时发现已经被占,就会放弃或者稍后重试
占坑一般使用 setnx(set if not exists)指令,只允许一个客户端占坑
先来先占,用完了在调用del指令释放坑
1 2 3 |
|
但是这样有一个问题,如果逻辑执行到中间出现异常,可能导致del指令没有被调用,这样就会陷入死锁,锁永远无法释放
为了解决死锁问题,我们拿到锁时可以加上一个expire过期时间,这样即使出现异常,当到达过期时间也会自动释放锁
1 2 3 4 |
|
这样又有一个问题,setnx和expire是两条指令而不是原子指令,如果两条指令之间进程挂掉依然会出现死锁
为了治理上面乱象,在redis 2.8中加入了set指令的扩展参数,使setnx和expire指令可以一起执行
1 2 3 |
|
redis解决超卖问题
1、使用reids的 watch + multi 指令实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
1)原理
1. 当用户购买时,通过 WATCH 监听用户库存,如果库存在watch监听后发生改变,就会捕获异常而放弃对库存减一操作
2. 如果库存没有监听到变化并且数量大于1,则库存数量减一,并执行任务
2)弊端
1. Redis 在尝试完成一个事务的时候,可能会因为事务的失败而重复尝试重新执行
2. 保证商品的库存量正确是一件很重要的事情,但是单纯的使用 WATCH 这样的机制对服务器压力过大
2、使用reids的 watch + multi + setnx 指令实现
1)为什么要自己构建锁
然有类似的 SETNX 命令可以实现 Redis 中的锁的功能,但他锁提供的机制并不完整
. 并且setnx也不具备分布式锁的一些高级特性,还是得通过我们手动构建
2)创建一个redis锁
在 Redis 中,可以通过使用 SETNX 命令来构建锁:rs.setnx(lock_name, uuid值)
. 而锁要做的事情就是将一个随机生成的 128 位 UUID 设置位键的值,防止该锁被其他进程获取
3)释放锁
锁的删除操作很简单,只需要将对应锁的 key 值获取到的 uuid 结果进行判断验证
. 符合条件(判断uuid值)通过 delete 在 redis 中删除即可,pipe.delete(lockname)
3. 此外当其他用户持有同名锁时,由于 uuid 的不同,经过验证后不会错误释放掉别人的锁
4)解决锁无法释放问题
1. 在之前的锁中,还出现这样的问题,比如某个进程持有锁之后突然程序崩溃,那么会导致锁无法释放
2. 而其他进程无法持有锁继续工作,为了解决这样的问题,可以在获取锁的时候加上锁的超时功能
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
|
优化锁无法释放的问题,为锁添加过期时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
关于redis中的锁
Watch:监测一个key。如果这个key的value改变,那个接下来的事务操作全部失效
multi: 开启一个事务。
Setnx: 跟set一样都往redis添加一个key。不一定的地方在于:set的时候如果这个值存在,就是修改操作。不存在就是添加操作。setnx:存在的时候不能再次添加,不存在的时候才能添加。
到此这篇关于详解redis中的锁以及使用场景的文章就介绍到这了,更多相关redis 锁内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!
原文链接:https://blog.csdn.net/ZPeng_Yan/article/details/106817375
本文链接:https://my.lmcjl.com/post/11221.html
4 评论