RedLock-红锁

RedLock-红锁,第1张

RedLock是什么?

RedLock是基于redis实现的分布式锁,它能够保证以下特性:

互斥性:在任何时候,只能有一个客户端能够持有锁;

避免死锁:当客户端拿到锁后,即使发生了网络分区或者客户端宕机,也不会发生死锁;(利用key的存活时间

容错性:只要多数节点的redis实例正常运行,就能够对外提供服务,加锁或者释放锁;

而非redLock是无法满足互斥性的,上面已经阐述过了原因。

RedLock算法

假设有N个redis的master节点,这些节点是相互独立的(不需要主从或者其他协调的系统)。N推荐为奇数~

客户端在获取锁时,需要做以下操作:

获取当前时间戳,以微妙为单为。

使用相同的lockName和lockValue,尝试从N个节点获取锁。(在获取锁时,要求等待获取锁的时间远小于锁的释放时间,如锁的lease_time为10s,那么wait_time应该为5-50毫秒;避免因为redis实例挂掉,客户端需要等待更长的时间才能返回,即需要让客户端能够fast_fail;如果一个redis实例不可用,那么需要继续从下个redis实例获取锁)

当从N个节点获取锁结束后,如果客户端能够从多数节点(N/2 + 1)中成功获取锁,且获取锁的时间小于失效时间,那么可认为,客户端成功获得了锁。(获取锁的时间=当前时间戳 - 步骤1的时间戳)

客户端成功获得锁后,那么锁的实际有效时间 = 设置锁的有效时间 - 获取锁的时间。

客户端获取锁失败后,N个节点的redis实例都会释放锁,即使未能加锁成功。

为什么N推荐为奇数呢?

原因1:本着最大容错的情况下,占用服务资源最少的原则,2N+1和2N+2的容灾能力是一样的,所以采用2N+1;比如,5台服务器允许2台宕机,容错性为2,6台服务器也只能允许2台宕机,容错性也是2,因为要求超过半数节点存活才OK。

原因2:假设有6个redis节点,client1和client2同时向redis实例获取同一个锁资源,那么可能发生的结果是——client1获得了3把锁,client2获得了3把锁,由于都没有超过半数,那么client1和client2获取锁都失败,对于奇数节点是不会存在这个问题。

在分布式版本的算法中,我们假设有N个Redis实例,这些节点是完全独立的,因此,我们不需要复制或任何其他的协调方法。在单机模式中,我们已经描述了怎样安全地取得和释放锁。我们认为Redlock算法在单机模式中将会使用这种方法去取得和释放锁是理所应当地。在我们地例子中N=5,这是一个合理地值,因此需要在5个不同的电脑或虚拟机上运行5个Redis实例以确保他们之间地相互独立。

为了取得锁,客户端执行以下操作:

1. 获取当前毫秒级时间Start。

2. 试着用相同的key和value顺序地对N个Redis实例进行加锁。该步骤中,在为每个实例设置锁的同时,客户端会使用一个超时时间timeout,timeout小于锁的自动释放时间ValidityTime。例如:如果ValidityTime=10s,则timeout应该在5~50ms之间。这可以使一个实例宕机时,客户端阻塞的时间更短,当一个Redis实例不可达时,应该尽快的去对另一个实例加锁。

3. 获取当前毫秒级时间End, End - Start = Elapsed, Elapsed是客户端为了获得锁而花费的时间。当且仅当客户端在所有实例中取得的锁的个数占多数,并且花费的总时间Elapsed小于锁的有效时间,此时,才会认为加锁成功。

4. 如果加锁成功,锁的有效时间则是ValidityTime = InitTime-Elapsed,Elapsed如步骤3中计算。

5. 如果客户端由于某些原因加锁失败,如ValidityTime<=0 或者 n <N/2 + 1。此时,客户端将会对这些实例进行unlock操作。

我们需要更好的说明这个排他锁的规则:只有当客户端在有效时间ValidityTime内完成工作才能保证该排他锁的有效性。

在上篇文章中聊到了分布式专家 Martin 对于RedLock的分析,他提出RedLock并不能保证安全性,而且系统依赖于时钟的准确性这不是一个优秀的分布式异步模型, Martin 对于RedLock有如下总结

Martin 为上述结论提出下面三种场景假设

假设存在实例A、B、C、D、E以及客户端1和2

提出RedLock没有类似fencing token的方案

== 注意:步骤2举例是GC造成的程序停顿从而导致安全性问题,这里也有可能是网络阻塞以及时间漂移 ==

论证依赖时钟假设不合理

注意: 在步骤2当实例C宕机时,而且锁数据没有持久化到磁盘时,也会出现上面的场景,所以RedLock才会提出延迟重启这个概念,间隔时间大于锁的过期时间,这时如果有时钟漂移问题,那么延迟重启是解决不了的。

论证网络延迟造成的安全性问题, 但需要注意这个场景假设是不合理的因为RedLock有处理

Martin 提出这些观点后,Redis的作者Antirez在博客上也作出了相应的回应,可以参考http://antirez.com/news/101,下面简单分析下

Antirez总结了Martin的问题,主要是如下两点

Antirez指出fencing方案是不合理的,fencing方案需要获取一个增量ID,而递增ID要求RedLock是一个可线性化的存储,而在大多数情况下对共享资源的操作并不是可线性化的,而RedLock虽然提供不了fencing方案那种递增的ID,但也可以产生随机字符串,这个随机字符串也是唯一的也就是Antirez文章中提到的unique token,Antirez举例可以实现Check and Set操作,原文如下,稍微有点难理解

简单点讲就是 自旋去执行CAS操作,只有相同时才更新

另外Antirez重点强调,也是质疑的一点

Martin提出的当锁失效后,客户端继续操作共享资源为了保证互斥,这时可以采用类似fencing的方案来避免,那为什么还需要使用一个分布式锁并且还要要求它有那么强的安全性保证呢?完全可以直接使用fencing方案。

到这里Antirez反驳了Martin提出的fencing方案,RedLock中有其它方案保证。

Antirez首先指出RedLock并不是强依赖时钟,允许有一定误差最大是10%,原文如下

Martin担心的时钟漂移问题有两点

对于Martin的担心Antirez指出

到这里如果认为Antirez对时钟漂移分析的合理,那么后面的分析也是合情合理。

Antirez首先指出,Martin提出的后面两种场景,其中一种是犯了一个大错的,这就是前面提到的第三个场景,因为GC pause引起,导致锁实例和客户端之间有长时间的消息延迟,这个情况RedLock是能处理的,先回顾下RedLock算法。

在Martin提出的场景中,就是在第2步加锁时客户端与Redis实例中存在长时间的消息延迟,这种场景可以在第4步被检测出来,不会让客户端拿到已经过期的锁,当然这种场景依赖于系统的时钟没有大的跳跃情况下,这也就是为什么Antirez会解释依赖时钟的可行性。

有人指出超时可能发生在第3步后,也就是获取时间T2后,就是Martin提到的第二个场景,Antirez承认会有这个问题,同时给出下面的解释

Redis的作者Antirez根据Martin提出的问题,做出了分析,总结如下

到这里两位大佬的博客就分析完毕了,如果有机会建议都看看这两篇文章!

博客原文参考

https://martin.kleppmann.com/2016/02/08/how-to-do-distributed-locking.html

http://antirez.com/news/101

分析参考

https://mp.weixin.qq.com/s/s8xjm1ZCKIoTGT3DCVA4aw

http://zhangtielei.com/posts/blog-redlock-reasoning-part2.html


欢迎分享,转载请注明来源:夏雨云

原文地址:https://www.xiayuyun.com/zonghe/26175.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-02-17
下一篇2023-02-17

发表评论

登录后才能评论

评论列表(0条)

    保存