客户端反复提交?
定时工作执行了多遍?
不必放心,分布式锁来帮您
当多个不同线程拜访同一资源时会造成竞态关系,也会呈现诸如资源被解决两次等恶性事件的产生。所以须要一种机制保障某一资源在某一时刻只会被惟一的服务解决。至此分布式锁服务机制油然而生。
实现分布式锁服务的形式很多,通常分布式锁服务须要具备三种个性
- 排他性:即对于锁来说任意时刻,能且仅能有一个用户能够取得锁
- 防止死锁:保障不会死锁这种状况,即使是在开释锁之前
- 容错性:只有服务集群有大多数节点存活就能够保障加解锁操作
Redis实现形式
Redis分布式锁官网文档:Distributed locks with Redis
概述
Redis因为其效率高操作简略等长处曾经成为了目前最为广泛的一种实现形式了。
作为一种kv数据库,Redis保障了key为全局惟一,实现形式也较为简单。
在版本2.6.12之前须要两条命令来创立一个具备时效性的不能被笼罩的记录作为锁
SETNX lock 1 // 创立一个key为lock,value为1的值,并禁止笼罩
EXPIRE lock 10 // 为key为lock的记录设置有效期10秒
然而办法因为须要两条语句毁坏了其原子性,导致会呈现一些不可意料的问题。在2.6.12版本后,退出了新的set的参数使得能够用一条语句实现上述的两条命令
SET lock 1 EX 10 NX
上面介绍一下在实在场景中会产生的问题
锁被非持有者销毁了?
避免其余线程误操把锁删除还须要用lua脚本实现原子性判断是销毁锁的线程是否为锁持有者,解决思路是不同的线程销毁锁时用本身的标示去销毁,lua代码思路如下:
if redis.call("GET",KEYS[1]) == ARGV[1]
then
return redis.call("DEL", keys[1])
else
return 0
end
解决数据的工夫太长被别的线程拿到锁了?
在理论状况中会有这样一种状况,AB两线程去拿锁,A胜B负,然而A解决的数据处理的好慢或者是在执行gc。这时锁曾经过期了,被B拿到了锁,B很快解决完了数据并曾经提交了。之后A解决完了数据并提交导致数据被解决了两遍。
尽管能够冗余过期工夫或者应用守护过程主动为其续时然而这两种计划也有造成死锁的可能。
如何解决这种问题请大伙在评论区留言通知我,谢谢。
Redis集群模式下用锁?
在集群的Redis环境下应用分布式锁状况会更简单一点,如某线程在Redis的主节点set lock胜利,但主节点解体而数据尚未同步給其余节点时,这时锁的作用就隐没了。
为了解决这个问题,Redis的作者设计出Redlock计划来应答主从切换后锁生效的问题。Redlock设计基于两个前提
- 部署形式采纳多个实例独自部署
- 节点要多,官网举荐5个以上
而上锁过程也改为了 - 获取以后的unix工夫
- 顺次对各Redis各节点应用雷同的key和全局惟一的value进行上锁,并设置小于锁过期工夫的超时工夫
- 仅当半数以上的节点返回胜利并且应用的工夫小于锁生效工夫才视为上锁胜利
- 若半数以上的节点未上锁胜利则须要对上锁胜利的节点进行解锁操作
Redlock的设计实质上还是基于分布式系统中的容错问题,即只有大多数节点可用,那么整个零碎便还可对外提供服务。
对于Redlock计划还引出业界两位大佬antirez和Martin的争执,在此就不多赘述,Martin对于分布式锁的阐述可见咋做分布式锁。
Zookeeper实现形式
Zk是一个分布式的文件服务零碎,Znode是其根底的节点,也是实现分布式锁服务机制的要害,对于zk来说Znode的门路是具备唯一性的,这便为分布式锁的互斥性提供了反对;创立节点时可应用-e参数使其节点成为长期节点,长期象征当此次连贯断开后改节点将自行销毁,这样如果某个线程在解决数据时出现异常解体会主动开释该锁,不会呈现死锁的状况;最初就是zookeeper集群也是保障高可用的,这样只有半数也上的zk还在失常工作就不会呈现问题。
理论流程为:
- 线程A、B同时创立/lock节点(create -e /lock 1)
- 线程A拿到锁,开始解决数据;线程B未拿到锁注册监听器,期待告诉
- 线程A解决数据结束,删除/lock节点(delete /lock),将锁销毁
羊群效应问题
现实情况中会有多个线程尝试去获取锁,未获取到的失败者们将对立注册watcher,这样须要告诉到各个线程时也须要将音讯推送给所有的线程,造成大量的资源节约。然而实际上咱们并不需要告诉給所有线程,只须要告诉給一个线程就够了。
所以在应用中会应用长期的程序节点来作为锁的实现,不同线程监听前一个节点的数据,实现排队监听。防止每个告诉都发給多个线程。
与Redis计划比拟
- 因为Zookeeper自身作为提供分布式协调性能,所以自身具备高可用性、强一致性等能力,所以锁的模型绝对Redis来说更加强壮
- 因为Watcher的存在,防止了未拿到锁的线程不停重试造成资源节约
- 因为Zookeeper创立节点与销毁节点的操作要比Redis创立记录删除记录耗费更多,所以性能上弱于Redis计划
发表回复