分布式锁-RedLock

Unsafe Lock by Martin Kleppmann

先大胆下一个结论。

由于分布式环境的复杂性目前是没有完美的分布式锁实现方案的。无论是基于redis的RedLock还是基于zookeeper的分布式锁。

这一篇主要讨论RedLock

简介

基于redis集群的分布式锁🔐

简单实现

算法1

1 set lock_key fix_value NX

2 expire lock_key time

问题

a 假设client A 执行到步骤1,然后client A挂掉,那么永远无法执行到步骤2了,也就是说形成了死锁-安全性问题

b client A 执行完整个步骤,还未进入临界区,然后因为gc停顿或者其他原因停顿,然后锁自动过期了;client B执行完整个步骤,拿到锁,此时client A和client B都获得了🔐;更加严重的是,client A从阻塞状态恢复了,会去释放掉🔐,导致B的🔐也被释放了-安全性问题

c 单节点的redis存在挂掉的情况-活性问题

d 主备形式的redis集群,当主挂掉之后,备会顶上,但是redis的异步复制的,备顶上可能此时还没有主的数数据-安全性问题

初步解决

a 将步骤1和步骤2合并成一个原子操作

算法2

set lock_key fix_value NX PX time

b 将fix_value 换成 random value

释放锁的操作变成如下

算法3

unlock(key, value){

if(redis.call(“get”, key) == value) then

return redis.call(“del”, key)

else

return 0

}

上述操作是利用lua完成,为原子操作,含义为先查看下lock对应的value是否还是自己set进去的值,如果是则可以释放锁,否则认为锁已经被别人占用

上述方式还是无法解决b的第一个问题,但是可以解决b的第二个问题

c 和 d的问题需要完成巨大的变更才有可能解决

RedLock

算法描述

基于多master的redis集群,保证在大多数节点存活的情况下锁的活性和安全性问题

定义

a 锁的超时时间timeout-client需要在这个给定时间内获取到锁,否则认为是超时的

b 锁的有效时间lock validity time-client获得锁后,在这个时间段后需要释放锁

a是远小于b的

c 锁节点的个数

加锁过程

1 记录下当前时间戳

2 依次向redis集群中执行算法2的操作,对于单个节点来说,如果在a的时间内获得🔐,则认为获得此节点的锁,反之亦然。

3 判断是否获得大多数🔐,并将此时的时间戳减去步骤1的时间戳得到b1,如果b1大于b的时间,则认为获取失败,触发释放锁逻辑,否则认为获取锁成功,锁的时间长度为b的时间减去b1的时间长度。

释放锁过程

依次向每个redis节点发出算法3的逻辑

问题分析

1 b的第一个问题还是无法解决

2 由于时钟跳跃会引发新的问题,某个节点已经被client获取了锁,但是因为时钟跳跃问题,锁迅速的过期了,那么可能存在另一个client来获取锁时,此节点会将锁分配给他,那么等于说一个节点被两个client同时成功获取锁了。

Martin认为

出于效率来使用分布式锁,允许偶尔的🔐失败,仅仅需要简单的分布式锁实现方案就可以了,RedLock的实现偏重

出于正确性来使用分布式锁,redLock会因为这样那样的分布式问题,而无法达到正确性的要求

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容