- 普通的分布式锁实现 依靠简单的lua脚本;但是缺点是redis单点故障问题无法解决;如果主从架构也无法保证主挂了之前主从是完全同步的
- 执行时间超过了锁的过期时间;需要启动一个看门狗定时器,再不停的续期;以保证当前线程未处理完成之前不会导致分布式锁失效
- 为了解决redis单点故障问题,这里引入了redissionLock;依赖几个没有任何主从关系的redis节点
- Redlock的实现如下:
1、获取当前时间 2、依次获取N个节点的锁。每个节点加锁的实现方式同上。这里有个细节,就是每次获取锁的时候的过期时间都不同,需要减去之前获取锁的操作的耗时: 比如传入的锁的过期时间为500ms; 获取第一个节点的锁花了1ms,那么第一个节点的锁的过期时间就是499ms; 获取第二个节点的锁花了2ms,那么第二个节点的锁的过期时间就是497ms; 如果锁的过期时间小于等于0了,说明整个获取锁的操作超时了,整个操作失败。 3、判断是否获取锁成功。如果client在上述步骤中获取到了(N/2 + 1)个节点锁,并且每个锁的过期时间都是大于0的,则获取锁成功,否则失败。失败时释放锁。 4、失败的话,需要删除所以节点的锁记录; 获取失败的节点也需要同步删除操作,因为防止网络抖动引起的响应超时
- 分布式锁的坑
1、性能问题 1)获取锁的时间上。如果redlock运用在高并发的场景下,存在N个master节点,一个一个去请求,耗时会比较长,从而影响性能。但是可以并行处理的,不过还是得预估好获取锁的时间,保证锁的TTL > 获取锁的时间+任务处理时间 2)红锁肯定更加耗用资源,以及降低并发量; 可以通过分段锁的思想降低锁的粒度;提高并发量 考虑批量加锁的场景,批量加锁防止并发操作形成无法加锁成功 2、重试的问题 多个client同时需要获取相同的分布式锁;这时就很大程度会导致,每个client都获取了部分节点锁,但是又都没有获取半数以上的情况;导致获取失败;失败后重试,这个重试的时间间隔最好随机化一点,这样可以获取成功的机会大大增加 3、节点宕机 比如redis节点有 A B C D E 五个节点 系统甲获取 ABC三个节点成功了;但此时C挂了 系统乙获取 DE成功了;此时C重启成功了 这样系统乙 CDE三个节点成功了 导致 多个系统都获取分布式锁成功了 解决方案:延迟重启(锁失效后再次重启) 或者增加节点数量 4、任务执行时间超过锁的TTL Client1获取到锁; Client1开始任务,然后发生了STW的GC,时间超过了锁的过期时间; Client2 获取到锁,开始了任务; Client1的GC结束,继续任务,这个时候Client1和Client2都认为自己获取了锁,都会处理任务,从而发生错误。 解决方案 一种解决方案是不设置TTL,而是在获取锁成功后,给锁加一个watchdog,watchdog会起一个定时任务,在锁没有被释放且快要过期的时候会续期 5、系统时钟漂移