Redis分布式锁
关于分布式锁
关于分布式锁,一般有三种选择,
1、redis
2、zk
3、DB锁(悲观锁、乐观锁)
乐观锁加版本号,悲观锁加for update
超卖问题演变
1.使用同步代码块,使用多个服务器集群,就不行了
使用nginx做负载均衡
2.使用redis的setnx
)]
问题:
进行执行代码块的时候,会导致出现异常,然后无法删除key,导致之后的线程一致无法进行抢购
解决办法;
使用finally,防止死锁
出现宕机
解决办法:给key设置过期时间
但是设置key,设置过期时间不是原子操作,所以也不行,就使用一条命令同时设置就可
在高并发的场景下,失效时间好,线程1执行时间刚刚好到一半业务就过期了
其他线程2进来执行,然后线程1删除锁,线程3进入,没法控制线程的进入顺序,导致线程混乱---------线程2的锁被线程1删除掉
解决办法给每个线程加一个线程id的标识,这样根据id匹配进行删除
这样就可以自己删掉自己的锁,不会导致自己删掉别人的锁问题存在
存在问题:还是时间问题,导致多个线程同时执行一段并发代码,导致资源数据更改错误问题
我们可以弄一个分线程,然后执行定时任务(每隔10s),判断主线程是否还存在执行
,就给它“续命”延长30s时间,防止主线程因为操作过长导致失效key
当发现主线程不存在,则分线程结束 ----实现非常麻烦
进入redisson分布式锁主题
引入依赖
初始化客户端
多种模式
修改原代码
分布式实现原理
线程1进行加锁操作,使用setnx进行设置
线程2进行加锁,发现已经有人加锁了,加锁失败,然后进行尝试自旋
开启一个分线程也就是后台线程,如果设置key的过期时候为30s,分线程执行每隔10s执行一个定时任务,判断线程是否还持有锁,有则进行延长,没有持有锁,则分线程结束
lua脚本执行命令的时候,会把这些代码当成一条命令去执行,所以不存在原子性问题
底层实现了lua执行脚本的原子性,要么同时成功,要么同时失败
点击进入任务看看
问题:
在高并发的场景下,使用redis主从节点,主节点进行接收数据,然后异步到从节点
在主节点接收线程1数据的时候,还没有异步到从节点时,主节点挂掉了,那么从节点作为下一次访问的主节点,线程3也要对线程1操作的某个商品id进行加锁操作,这时原来的从节点没有对应的数据信息进行操作
分布式锁解决
解决办法使用zookeeper集群
CAP原则又称CAP定理,指的是在一个分布式系统中,
一致性(Consistency)、
可用性(Availability)、
分区容错性(Partition tolerance)。
CAP 原则指的是,这三个要素最多只能同时实现两点,不可能三者兼顾。
对于redis分布式锁满足ap,redis主节点加锁成功后立马返回给客户端,可用性高
对于zookeeper满足cp,
zookeeper的话有leader主节点,多个从节点,当加锁成功后,会先将主节点的数据同步到从节点,当有半数的key同步成功后,才会返回给从节点(从节点同步成功后告诉主节点,主节点会根据从节点数量进行判断是否满足一半数量同步成功),牺牲一点可用性来进行数据同步一致性
当主节点挂掉了,底层有个zab原子网络协议,会选取一个节点作为新的leader节点
关于redis分布式锁和zookeeper的选择?
高并发的话选择redis,主从架构出现的问题概率小,出错了的话,写一个脚本进行判断人工补偿
如果要避免这些问题,就要使用zookeeper,但是并发没有redis的高
RedLock原理
底层类似zookeeper,加锁发送setnx会向所有从节点发送,当其中一个失败全部回滚
性能问题,本身也不能保证失效问题,不推荐使用
补偿
如果要避免这些问题,就要使用zookeeper,但是并发没有redis的高
加锁是串行执行的,超级高并发的情况速度比较慢,还可以进行优化