分布式锁和事务
1.分布式锁的实现方式?
1.基于数据库实现 -mysql行锁
2.基于zookeeper -CP模式
3.基于Redis setnx实现 -AP模式
4.Redis框架 Redission,RedisLock
要求:保证一致性 zk实现分布式锁
保证可用性 redis实现分布式锁
2.Zookeeper实现分布式锁
原理:zookeeper节点路径不能重复,保证唯一性,通过临时节点+事件通知来实现。
多个jvm在zk上同时创建一个相同的临时节点,有zk节点路径的唯一性,只有一个jvm会创建成功,其他jvm会尝试重试,如果尝试多次创建临时节点失败。就会订阅当前临时节点,然后变为阻塞状态,当jvm01业务执行完释放锁(调用close()方法)因为是临时节点,所以调用close方法时就会删除zk上对应的临时节点,因为在zk上有事件通知,删除掉节点会同时向订阅了当前临时节点的其他jvm发送通知,唤醒其他正在阻塞的jvm参与锁的竞争。
获取锁成功但是由于业务逻辑超时一直不释放情况下怎么解决?
依靠zk自身的sessionTimeout来删除节点释放锁,防止其他的jvm一直阻塞等待,将业务代码全部回滚,使用手动事务。
zookeeper实现分布式锁如何避免羊群效应?
羊群效应:当参与同一个锁竞争的jvm数量很大时,第一个jvm获取到锁,逻辑执行完释放锁,这时候zkServer就要去唤醒其余的数量很大的阻塞状态的jvm,可能唤醒到一半的时候其他进程已获取到锁,成本很高,所以要采用临时顺序节点的方式。
- 基于临时顺序节点实现分布式锁:
先创建一个根路径,多个jvm在根路径下创建多个顺序节点,当某个jvm创建的临时顺序节点最小时,代表当前jvm获取锁成功,jvm02获取锁失败,就会订阅前一个节点,即jvm01,当01释放锁删除01节点后,jvm02成为最小的临时顺序节点,获取锁成功,jvm03以此类推,订阅前一个节点,当前一个节点释放锁,删除节点,当前节点变为最小的临时顺序节点,获取锁成功。相当于juc中的公平锁,这样在唤醒的时候不必唤醒所有的节点,只需唤醒一个节点即可。
3.Redis如何实现分布式锁?
1.redis使用setnx实现:
- 获取锁的流程:多个jvm在redis写入一条setnx相同的key,谁能够写入成功,谁就可以获得锁。获取锁失败的时会短暂重试,重试多次失败,当前jvm会阻塞。
- 如何避免死锁:在setnx的时候为key设置一个过期时间,过期时间之后key自动被删除,锁释放。
- 获取到锁的jvm的业务执行时间>key的过期时间,该如何处理?:可以采用延时过期方法,如果业务代码在某段时间内没有执行完毕,就将对应的key的过期时间延长。但是也不能一直延长,容易产生死锁,可以设定延长过期的次数,超过这个次数的话回滚业务代码。(后台开启一个定时任务线程,检查业务代码执行完毕之前的key的过期状态,没执行完毕的情况下去延长key的过期时间,看门狗机制)。