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. 客户端需要重试获取锁,为了资源竞争,他们重试的等待时间应该大于需要从大多数实例上获取的时间

复制代码

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

查看所有标签

猜你喜欢:

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

梦断代码

梦断代码

Scott Rosenberg / 韩磊 / 电子工业出版社 / 2008.06 / 49.00元

软件乃是人类自以为最有把握,实则最难掌控的技术。本书作者罗森伯格对OSAF主持的Chandler项目进行田野调查,跟踪经年,试图借由Chandler的开发过程揭示软件开发中的一些根本性大问题。. 本书是讲一事,也是讲百千事;是写一软件,也是写百千软件;是写一群人,也是写百千万人。任何一个在软件领域稍有经验的技术人员看完本书,必掩卷长叹:做软件难。...一起来看看 《梦断代码》 这本书的介绍吧!

随机密码生成器
随机密码生成器

多种字符组合密码

MD5 加密
MD5 加密

MD5 加密工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具