Redis分布式锁(Redlock官方文档的理解)

栏目: 数据库 · 发布时间: 5年前

需要设计合理分布式锁,并满足基本的保障
1.安全 -> 互斥属性,在任何条件下,只允许一个客户端拿到锁
2.活跃度(可靠性)A -> 死锁检测,即使有获取到锁的客户端崩溃或者不可用,但是最终锁还是能被获取到
3.活跃度(可靠性)B -> 容错,只要大部分的 redis 节点都存活,客户端就能够获取锁和释放锁
复制代码

为什么故障恢复的实现还不足够

有个Master + slave
1.客户端A获取锁
2.master 在命令传播给slave前就崩溃了
3.slave这时候还不存在key,所以当它被晋升成master时
4.客户端B尝试获取锁,就被获取到了-> 这时候就不满足redis分布式锁的安全性了

在极端情况下,集群服务发生失败时,多客户端可能同时获取锁
复制代码

单实例的正确实现

在解决上述问题前,先把基本的redis分布式锁的设计做好

当我们获取锁的时候,执行下面命令行:

SET resource_name my_random_value NX PX 30000
NX表示当key不存在才能设置成功
PX表示超时时间 
my_random_value 需要在所有客户端和获取锁的请求中表示唯一
复制代码

释放锁,需要带着 my_random_value 作标识然后再del,2个操作作原子,所以推荐使用 lua 脚本

if redis.call("get",KEYS[1]) == ARGV[1] then
    return redis.call("del",KEYS[1])
else
    return 0
end
复制代码

说明

1.这样就能有效的阻止其他客户端生成锁。(应该不用多说明吧。)
2.my_random_value 应该怎么设置,反正容量越小,消耗越小越好,文档上给出是使用clientId加上时间戳或者使用RC4算法
3.expire的时间设置,我们称作锁有效时间,是锁自动释放时间 + 客户端需要在锁时间内执行的事务所需要的时间(在其他客户端获取到锁之前)
= 锁自动释放时间 + 客户端处理业务(锁期间)的时间
考虑到互斥的保证,这个时间窗口应该限制在锁被获取之后

复制代码

基本操作介绍了,现在可以优化了

Redlock算法

在分布式版本中,我们假设我们有N个redis 的matser,各自独立,没有任何关系(不在一个cluster下),可以部署在不同的服务器或是虚拟机上,假设N设置成5

客户端获取锁的操作:
1. 客户端A获取当前时间戳
2. 客户端A尝试在所有N个redis中获取锁(有序的),使用同样的key和value,这一步中,由于需要遍历去请求多个redis服务(set的命令请求,之前理解成业务的处理时间),可能导致阻塞,需要设置超时时间,假设锁自动释放的时间是10s,那么这个超时时间可以设置在 5-50毫秒范围,这一步,防止客户端在获取锁期间由于redis节点崩溃不可用导致获取锁曹氏
3. 客户端A获取锁时,再获取当前时间戳,与步骤1的时间相减,得到获取锁消耗的时间,在锁有效期内,当且仅当客户端A拿到大部分(至少3)的锁时,分布式锁才可以被正式获取
4. 每次当锁被获取到时(从每个master获取),有效时间可以被设置成初始有效时间减去获取锁消耗的时间 (因为已经获取锁了,所以获取锁的时间不用算进去)
5. 假设在步骤2中客户端A获取不到锁,比如拿不到大部分的锁,或者是锁还没超时,它需要把自己在少部分redis上拿到的锁释放掉

复制代码

这个算法是异步的吗

这个算法依赖的一个假设:是不同进程的各自的时钟精确率(就是表走得快走得慢,而且误差跟锁释放时间比小得多)一样,而且不(需要)作时间的同步。类似于,现实生活中,每个人都各自使用自己的电脑(以及电脑上的时间),通常也不会有什么问题;
在这个情况下,我们需要更具体的说明互斥规则:它(互斥规则)保证仅仅只有获取锁的哭护短能够在锁的有效时间内结束它的工作,减去某个很小的时间差(几毫秒,用来作补偿)

复制代码

失败重试(这里指的是获取锁的失败)

当客户端获取不到锁,它应该在之后一个随机时间点重试,这为了避免多个客户端尝试同时获取同一个资源(类似脑裂的情况,大概意思就是竞争了一堆,却发现,没人拿到锁),客户端拿到锁越快(早),脑裂的情况越小(或者重试的需要越小),所以理想情况下,客户端可以同时(多路复用)发送set命令给各个master

复制代码

释放锁

锁释放步骤很简单,就是把所有master实例上的锁释放,并不需要关心客户端在该实例上有没有成功得到锁

复制代码

安全讨论

假设一个客户端能够获取大多数实例上的锁,所有实例都会存在一个key,并且有个相同的超时时间,但是这个key的设置的时间点是不一样的(就是set的时候都不一样,因为是顺序执行,总会有时间差),所以key会在不同的时间超时。但是当第一个key被至少设成T1,最后一个key被设成T2(超时时间),我们可以确认第一个key会存在至少 MIN_VALIDITY=TTL-(T2-T1)-CLOCK_DRIFT 的时间,而且其他的key会失效的更晚,我们要同时将时间设置成这个
当一个key被set(NX)的时候,其他key就无法被别的客户端set了

----
总的说的就是时间设置还有并发控制
复制代码

可靠性讨论

1. 自动释放的锁最终还能被获取到;
2. 客户端通常会协助删除那些没获取到的锁(步骤5)、锁获取到并且工作结束的,使得并不需要等待锁超时才能重新获取锁;
3. 客户端需要重试获取锁,为了资源竞争,他们重试的等待时间应该大于需要从大多数实例上获取的时间

复制代码

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

如何求解问题

如何求解问题

Zbigniew Michalewicz、David B.Fogel / 曹宏庆 / 中国水利水电出版社 / 2003-2-1 / 35.00元

《如何求解问题:现代启发式方法》通过一系列贯穿于章节间的有趣难题,《如何求解问题:现代启发式方法》深入浅出地阐述了如何利用计算机来求解问题的一些现代启发式方法。全书包括两部分,共分15章。一起来看看 《如何求解问题》 这本书的介绍吧!

RGB转16进制工具
RGB转16进制工具

RGB HEX 互转工具

XML 在线格式化
XML 在线格式化

在线 XML 格式化压缩工具