————————————
分布式锁有哪些实现?
1.Memcached分布式锁
使用Memcached的add命令。这个命令是一个原子操作。只有当密钥不存在时,添加才能成功,这意味着线程被锁定。
2.Redis分布式锁
与Memcached类似,使用Redis的setnx命令。这个命令也是原子操作。只有当密钥不存在时,集合才能成功。(setnx命令并不完美,稍后将介绍替代命令。)
3.动物园管理员分布式锁
利用动物园管理员的顺序临时节点,实现了分布式锁和等待队列。Zookeeper最初是为了实现分布式锁服务而设计的。
4.圆胖的
Google的粗粒度分布式锁服务,底层使用Paxos一致性算法。
如何用Redis实现分布式锁?
Redis分布式锁的基本流程并不难理解,但是要写得完美却不是那么容易。在这里,我们需要理解分布式锁实现的三个核心元素:
1.锁
最简单的方法是使用setnx命令。钥匙是锁的唯一标识符,根据业务命名。比如你想锁定一个商品的尖峰活动,可以把钥匙命名为“lock_sale_ commodity ID”。值设置为什么?就设成1吧。锁定的伪代码如下:
setnx(键,1)
当一个线程执行setnx并返回1时,表示密钥原本不存在,线程成功获得锁;当一个线程执行setnx并返回0时,表示key已经存在,线程抢锁失败。
2.用钥匙开锁
有锁就要开锁。当被锁定的线程完成任务时,它需要释放锁,以便其他线程可以进入。释放锁最简单的方法是执行del指令,伪代码如下:
del(键)
释放锁后,其他线程可以继续执行setnx命令来获取锁。
3.锁定超时
锁超时是什么意思?如果一个被锁定的线程在执行一个任务的过程中挂起,并且显式释放锁已经太晚了,那么这个资源将永远被锁定,其他线程也永远不会再进来。
所以setnx的key必须设置一个超时,以保证锁在一定时间后即使没有明确释放也会自动释放。Setnx不支持超时参数,所以需要额外的指令。伪代码如下:
过期(密钥,30)
总而言之,我们的分布式锁实现的第一个伪代码如下:
if(setnx(key,1)= 1){
过期(密钥,30)
尝试{
做某事......
}最后{
del(键)
}
}
代码结尾不错,怎么回家等通知?
因为上面的伪代码有三个致命的问题:
1.集合NX和过期的非原子性
想象一个极端的场景,当一个线程执行setnx时,它成功地获得了锁:
Setnx刚刚成功执行,但是在它能够执行expire命令之前,节点1 Duang挂起。
这样,锁没有设置到期时间,变成“不死”,其他线程就无法再获得锁。
怎么解决?Setnx指令本身不支持传入超时。幸运的是,REDIS 2 . 6 . 12版或以上版本给set指令增加了可选参数,伪代码如下:
设置(键,1,30,NX)
这样就可以替换setnx命令了。
2.del导致错误删除
另一个极端的情况是,如果一个线程成功获得锁,并且设置的超时时间是30秒。
如果线程A由于某种原因执行缓慢,30秒后没有完成,那么锁过期自动释放,线程B得到锁。
然后,线程a完成执行任务,然后线程a执行del指令释放锁。但此时线程B还没有执行完,线程A实际上删除了线程B添加的锁。
如何避免这种情况?您可以在del释放锁之前进行判断,以验证当前锁是否是自添加的。
至于具体实现,可以在锁定时以当前线程ID为值,在删除前验证key对应的值是否是自己线程的ID。
锁定:
string threadId = thread . Currentthread()。getId()
set(key,threadId,30,NX)
解锁:
if(ThreadId . equals(RedIsclient . get(key))){
del(键)
}
然而,这意味着一个新的问题:判断和释放锁是两个独立的操作,而不是原子性。
我们都是追求完美的程序员,所以这一块应该用Lua脚本来实现:
string LuAscript = " if redis . call(' get ',KEYS[1]) == ARGV[1]然后returnredis.call('del ',KEYS[1])else return 0 end ";
redisClient.eval(lua,Collections.singletonList(key),collections . singletonlist(threadId));
这样,验证和删除过程就是一个原子操作。
3.并发的可能性
在刚才第二点描述的场景中,虽然我们避免了线程A误删键的情况,但是有两个线程A和B同时访问代码块,这还是不完善的。
我该怎么办?我们可以让获得锁的线程打开一个守护线程来“延长”即将到期的锁。
29秒后,线程A还没有执行完。此时,守护线程将执行expire指令来“更新”锁20秒。守护线程从第29秒开始执行,每20秒执行一次。
当线程a完成任务时,它将显式关闭守护线程。
另一方面,如果节点1突然断电,守护线程将停止,因为线程A和守护线程在同一个进程中。锁超时没人续,自动解除。
守护线程的代码不难实现。有了大致的思路,你可以尝试自己去实现。
ID:zxsycjh
1.《setnx 漫画:什么是分布式锁?》援引自互联网,旨在传递更多网络信息知识,仅代表作者本人观点,与本网站无关,侵删请联系页脚下方联系方式。
2.《setnx 漫画:什么是分布式锁?》仅供读者参考,本网站未对该内容进行证实,对其原创性、真实性、完整性、及时性不作任何保证。
3.文章转载时请保留本站内容来源地址,https://www.lu-xu.com/caijing/1014846.html