ZooKeeper实现分布式锁

ZooKeeper实现分布式锁

leo 519 2021-04-10

ZooKeeper 的数据模型

ZooKeeper的数据模型类似于数据结构中的,也很像文件系统

ZooKeeper的数据模型基于节点,称为Znode。不同于树的节点的是,Znode的引用方式通过路径引用,类似于文件系统。数据都存储在内存中。

znode示意图

ZNode分为四种类型:

ZNode的四种类型

  • PERSISTENT:持久节点,默认的节点类型。创建节点的客户端与 Zookeeper 断开连接后,该节点依旧存在。
  • EPHEMERAL: 临时节点。和持久节点相反,当创建节点的客户端与 Zookeeper 断开连接后,临时节点会被删除。
  • PERSISTENT_SEQUENTIAL:持久顺序节点。结合持久节点和顺序节点特性。
  • EPHEMERAL_SEQUENTIAL:临时顺序节点。结合临时节点和顺序节点特性。

所谓顺序节点,就是在创建节点时,Zookeeper 根据创建的时间顺序给该节点名称进行编号。

Zookeeper实现分布式锁

Zookeeper 分布式锁是利用临时顺序节点实现的。

获取锁

首先创建一个持久节点,作为锁的父节点(LockParent)。当有客户端需要获取锁时,在父节点下添加临时顺序节点

示意图如下:

ZooKeeper获取锁

之后,客户端查找 LockParent 下面所有的临时顺序节点并排序,判断自己所创建的节点 Lock1 是不是编号最小的一个。如果是,则成功获得锁。

这时,如果再有一个客户端2 来获取锁,则在 LockParent下再创建一个临时顺序节点 Lock2。同样对 LockParent 下面所有的临时顺序节点排序,发现 Lock2 不是编号最小的节点,于是客户端2向排序在它前一个节点 Lock1 注册 Watcher,用于监听 Lock1 节点是否存在。这意味着客户端2获取锁失败,进入了等待状态。

ZooKeeper获取锁失败

释放锁

释放锁分为两种情况:

操作完成,显示释放

当客户端完成数据的操作后,显示删除自己创建的临时节点。

例如:客户端完成数据操作,显示删除Lock1。此时,由于客户端2对 Lock1 注册了 Watcher ,那么客户端2就会收到 Lock1 节点删除的事件通知,然后客户端2查找 LockParent 下面所有的临时顺序节点并排序,判断自己所创建的节点 Lock2 是不是编号最小的一个。如果是,则成功获得锁。如果不是,则向排序在它前一个节点 注册 Watcher 。

宕机

当客户端操作数据中途宕机了,那么就会断开与 ZooKeeper 服务器的连接,那么根据临时顺序节点的特性,节点会被自动删除。之后的操作就同显示删除一样了。

ZooKeeper 与Redis 分布式锁对比

分布式锁实现优点缺点
ZooKeeper1. 有封装好的框架,容易实现
2. 有等待锁的队列,提升获取锁效率
添加、删除临时节点效率低
RedisSet和Del执行执行效率高1. 实现复杂,需要考虑原子性、超时、误删等情况
2. 没有等待锁的队列,需要客户端自旋获取锁,效率低

注:等待锁的队列是指 LockParent 下的临时节点按照编号排序形成的列表,总是编号小(先创建)的先删除,类似队列FIFO特性。