建立一张锁表,当要锁住某个资源时,就在该表中增长一条记录,想要释放锁的时候就删除这条记录。html
Zookeeper 的分布式锁是经过临时节点(EPHEMERAL)实现的。当客户端会话终止或超时后 Zookeeper 会自动删除临时节点。 加锁流程: 假设锁空间的根节点为 /_locknode_node
zookeeper若是长时间没有检测到客户端心跳的时候就会认为Session过时,那么这个Session所建立的全部的临时节点都会被删除。
因此网络、jvm gc等缘由致使长时间没有收到心跳,会致使多客户端操做共享资源。redis
zookeeper在集群模式下,Client发的请求只会由Leader执行,发给Follwer的请求会转发给Leader。Leader接收到请求后会通知Follwer进行投票,Follwer把投票结果发送给Leader,只要半数以上返回了ACK信息就认为经过,则执行 commit ,同时提交本身。再返回给Client。
假设Follwer宕机,是不会转发到Leader,因此不会获取到锁。假设Leader宕机,就不会进行消息广播,会先进行选取新Leader再处理,因此也不会丢失信息。算法
Redis文档中描述了单实例实现分布式锁的正确方法:sql
//添加锁
SET resource_name my_random_value NX PX 30000
复制代码
//释放锁
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
复制代码
添加锁时,key 的值是 my_random_value (一个随机值),释放锁时先经过判断当前 key 的值是否等于指定的值,这样能够避免误删,而且经过lua脚本实现原子性操做。
若是不是原子性操做也会致使误删。del 命令若是因网络等缘由延时,这时恰好 key 过时被另外一个客户端获取了共享资源,而后 del 命令又到达 redis 实例致使误删。数据库
单实例有两个缺点,1. 如何设定过时时间,2. 没有高可用。apache
假设有一 Master 一 Slave ,当 Master 节点获取到锁以后,还没同步到 Slave ,Master 就宕机发生主备切换,Slave 晋升为 Master ,此时再申请锁时,就会获取到同一共享资源。服务器
对于这种状况,redis的做者antirez提出了RedLock算法(但依然没有解决设定过时时间的问题),获取大多数节点的锁就算加锁成功,流程以下(来自官方文档):
假设有N个Redis master(文档假设有5个,大多数就是大于等于3),这些节点彻底互相独立,不存在主从复制或者其余集群协调机制网络
崩溃恢复 假设有5个节点,如今客户端成功向3个节点加锁,由于大于等于N/2+1个Redis实例,因此加锁成功。这是3个节点中某一个节点宕机重启,则其余客户端有机会向重启节点和另外两个节点加锁。致使共享资源被屡次操做。
对于该状况,做者提出了延迟重启的方案,即当一个Redis 节点重启后,只要它不参与到任意当前活动的锁,为了达到这种效果,咱们只须要将新的 Redis 实例,在一个TTL时间内,对客户端不可用便可,在这个时间内,全部客户端锁将被失效或者自动释放。dom
[1] zookeeper.apache.org/doc/current…
[2] Distributed locks with Redis