Zookeeper是如何实现分布式锁的

Zookeeper是如何实现分布式锁的

标签 : Zookeeper 分布式redis


实现分布式锁要考虑的重要问题

1. 三个核心要素

加锁, 解锁, 锁超时数据库

2. 三个问题

  • 要保证原子性操做, 加锁和锁超时的操做要一次性执行完毕
  • 防止误删锁
  • 在误删的基础上, 加一个守护线程, 为锁续命.

什么是临时顺序节点


Zookeeper的数据存储结构就像是一棵树, 这棵树由节点组成, 这种节点叫作Znode. Znode分为四种类型.服务器

1. 持久节点(Persistent)

默认的节点类型, 建立节点的客户端和Zookeeper断开链接以后, 该节点依旧存在.框架

2. 持久顺序节点(Persistent Sequential)

所谓顺序节点, 就是在建立节点的时候, Zookeeper根据节点的建立时间顺序给节点的名称进行编号.分布式

顺序持久节点

3. 临时节点(Ephemeral)

和持久节点相反, 当建立节点的客户端与Zookeeper断开链接以后,临时节点会被删除.性能

建立临时节点
断开客户端
删除临时节点

4. 临时顺序节点(Ephemeral Sequential)

在建立节点时, Zookeeper根据建立的时间顺序给该节点名称进行编号; 当建立节点的客户端与Zookeeper断开链接以后,临时节点会被删除.线程


Zookeeper实现分布式锁的原理

上面已经说了Znode的四种类型, 其中最后一种类型 临时顺序节点 是最有利于实现Zookeeper分布式锁的.3d


1. 获取锁

  • 首先, 在Zookeeper当中建立一个持久节点ParentLock. 当第一个客户端想要得到操做某项数据的锁的时候,须要在该持久节点之下简历一个临时顺序节点Lock1.

客户端1尝试得到所

  • 以后, 客户端1 查找ParentLock下面全部的临时顺序节点并按照大小排序, 判断本身所建立的节点Lock1是否是顺序最靠前的一个. 若是是第一个节点,则成功得到锁.

客户端1成功得到所

  • 此时, 客户端2 前来获取该项数据的锁, 则在ParentLock下再建立一个临时顺序节点Lock2.

客户端2尝试得到锁

  • 客户端2 查找ParentLock下面全部的临时顺序节点并排序,发现本身的Lock2节点并非最靠前的. 因而客户端2向排序仅仅比它靠前的Lock1注册Watcher, 用于监听Lock1动态. 这意味着 Lock2抢锁失败, 进入等待状态.

Lock2抢锁失败

  • 此时, 若是有一个客户端3前来获取锁, 则在ParentLock下在建立一个临时顺序节点Lock3.

Lock3尝试得到锁

  • 客户端3查找ParentLock下面全部的临时顺序节点并排序, 判断本身所建立的节点Lock3是否是顺序最靠前的一个, 结果发现Lock3不是最靠前的. 因而客户端3一样抢锁失败, 进入了等待状态.

Lock抢锁失败

  • 这个时候客户端1获得了锁, 客户端2监听了客户端1, 客户端3监听了客户端2. 这样恰好造成一个等待的队列.

2. 释放锁

释放锁有两种状况code

2.1 任务完成, 客户端显示释放

  • 当任务完成时, 客户端1会显示的调用删除节点Lock1的指令.

客户端1释放锁

2.2 任务执行过程当中, 客户端崩溃

  • 得到锁的客户端1在执行任务的过程当中, 若是崩溃, 则会断开和Zookeeper服务器的连接, 根据临时节点的特性, 相关联的Lock1会随之自动删除.

客户端1崩溃,自动释放锁

3. 得到锁

  • 因为客户端2一直在监听Lock1的状态,这个时候发现Lock1注销了, 客户端2会当即接收到通知. 这个时候客户端2会再次查询ParentLock下的全部节点, 确认本身所建立的节点是否是最小的节点, 若是是最小的则成功得到锁.

客户端2成功得到锁

同理可推至客户端3


总结

Zookeeper和Redis分布式锁的比较

分布式锁 优势 缺点
Zookeeper 1.有封装好的框架,容易实现.
2.有等待锁的机制(Watcher),能够提升抢锁的效率,好处多多
添加和删除节点的性能比较低
Redis SetDel的性能比较高(毕竟键值数据库,Hash) 1.实现复杂,须要考虑原子性,误删等状况.2.没有等待锁的机制,只能经过客户端的自旋来等锁,效率低下.
相关文章
相关标签/搜索