除了Redis可以实现分布式锁之外,还可以通过Zookeeper实现,它较于前者的优势是它的数据是强一致的,一般秒杀会用到。今天我们来介绍一下基于ZooKeeper实现分布式锁。😄

首先在介绍ZooKeeper实现分布式锁之前,先介绍一下ZooKeeper遵循的一个ZAB协议。专为 ZooKeeper 设计,用于在分布式系统中实现原子广播和崩溃恢复。ZAB 协议的核心目标是确保所有节点在数据更新时保持一致,同时保证系统的高可用性和分区容错性。

ZAB协议

核心目标

  • 原子广播

其实就是原子性(大雾,所有写操作(如创建节点、修改数据)必须被所有节点成功接收,或者全部失败。

  • 顺序一致性

所有节点的数据更新顺序必须严格一致。

  • 崩溃恢复

如果 Leader 节点挂了,能快速选出新 Leader,并恢复数据一致性。(如果老板倒了,快速选新老板,对照员工的数据恢复,保证数据的一致性)

工作模式

有两种工作模式。

崩溃恢复模式

  • 所有节点(Follower)投票选一个新 Leader(类似选班长)。后面会说选举的具体过程。

  • 新 Leader 会检查所有节点的数据,确保大家的数据一致(同步最新状态)。

  • 同步完成后,集群进入消息广播模式

消息广播模式

  • 客户端发送写请求(比如创建节点)给 Leader。

  • Leader 把写操作包装成一个 Proposal(提案),按顺序广播给所有 Follower。

  • Follower 收到后,先回复 ACK(确认),但不立即执行。

  • 当超过半数 Follower 确认后,Leader 发送 Commit 命令,大家才真正更新数据。

关键点

  • 只有 Leader 能处理写请求(避免冲突)。

  • 写操作必须按顺序执行(防止数据错乱)。

在实现分布式锁之前,还需要知道在Zookeeper中的几个核心概念,才能对后续实现有所了解。

数据一致性保持

两阶段提交

  • 阶段1(Proposal):Leader 广播提案,Follower 只记录但不执行。

  • 阶段2(Commit):收到多数派确认后,Leader 才通知大家真正提交。

事务ID(ZXID)

  • 每个写操作都有一个全局唯一的 ZXID(类似版本号),一个64位的整数,由两部分组成:

    • epoch:前32位,Leader 的任期编号(换届时递增)。

    • counter:后32位,在这个周期内leader进行了多少次事务操作(每次新选举就 +1)。

  • 作用:确保所有节点按相同顺序执行操作。

核心概念

  • ZNode:​ ZooKeeper 存储数据的节点,类似于文件系统中的文件或目录。

  • 临时节点(Ephemeral Node):​ 客户端会话结束后,节点会自动删除。

  • 顺序节点(Sequential Node):​ 节点名称会自动附加一个递增的数字后缀。

  • Watcher:​ 监听 ZNode 的变化(如创建、删除、数据更新等),并触发回调。

可以这么想,zookeeper相当于是在一个热门的餐馆店排队的管理者。

然后znode就相当于是显示屏,显示当前的排队情况。

临时节点就相当于排队的那个小票,上面一般会标记一个排队号。

顺序节点就是说每个人取的号是递增的,先到先到。

watcher就相当于广播,盯着当前最小的顺序节点,只要它被删除,你就喊下一个快点上去。

现在我们知道了Zookeeper的核心概念了,那么就要去了解它到底是怎么实现的分布式锁了,接下来主要介绍实现的原理。

实现原理

临时顺序节点

临时顺序节点=临时节点+顺序节点

特点:

  • 临时性

  • 顺序性

  • 唯一性

Watcher机制

ZooKeeper 提供的一种事件通知机制。客户端可以在某个 ZNode 上注册一个 Watcher,当该 ZNode 发生变化时(如创建、删除、数据更新等),ZooKeeper 会通知客户端。

特点:

  • 一次性

  • 异步

  • 可以监听多种事件

接下来具体说明实现的流程了。

实现流程

  • 创建一个节点lock作为锁的根节点,当有线程需要抢锁的时候在该节点下创建一个临时有序节点​创建临时顺序节点。

  • 获取创建的lock的所有子节点,并按照顺序排序,是最小的就获得锁,不是就监听前一个节点的删除事件。

  • 业务逻辑完成后,删除创建的临时节点,通知下一个序号的节点,让它得到锁

根据实现的流程就能知道,使用zookeeper不会造成死锁,还会自动释放锁,同时数据也是强一致的。

ZAB选举leader

zookeeper中有三种节点,leader,follower,observer(不参与选举)。

集群启动时,会选举,然后leader挂了也会选举。

如果 Leader 挂了,集群会暂时不可写(但可读),必须快速选一个新 Leader 出来,否则整个系统就卡住了。

这里因为只去了解了快速选举的算法,就先介绍快速选举,后续增加。😎

核心规则

比较ZXID,节点的ZXID大就投票给它,相同的话,就在去比较myid,也是谁大投谁。然后能投票给自己或其他的节点,投票的信息有ZXID,myid,epoch。

选举流程

  • 初始化所有节点都进入LOOKING状态

  • 每个节点首先投票给自己,然后广播投票信息给其他节点

  • 节点接收其他节点的投票信息,根据选举规则比较投票信息

  • 节点统计收到的投票信息,判断是否有节点获得了超过半数的投票,如果有,选举结束,它就是leader了☝️

  • 新leader向其他节点发送确认信息,其他节点收到信息后,进入FOLLOWING状态,成为follower

  • leader处理请求,广播到follower🤝