面试必备:聊聊分布式锁的多种实现!(二)
4. Zookeeper分布式锁
在学习Zookeeper分布式锁之前,我们复习一下Zookeeper的节点哈。
Zookeeper的节点Znode有四种类型:
• 持久节点:默认的节点类型。创建节点的客户端与zookeeper断开连接后,该节点依旧存在。
• 持久节点顺序节点:所谓顺序节点,就是在创建节点时,Zookeeper根据创建的时间顺序给该节点名称进行编号,持久节点顺序节点就是有顺序的持久节点。
• 临时节点:和持久节点相反,当创建节点的客户端与zookeeper断开连接后,临时节点会被删除。
• 临时顺序节点:有顺序的临时节点。
Zookeeper分布式锁实现应用了临时顺序节点。这里不贴代码啦,来讲下zk分布式锁的实现原理吧。
4.1 zk获取锁过程
当第一个客户端请求过来时,Zookeeper客户端会创建一个持久节点locks。如果它(Client1)想获得锁,需要在locks节点下创建一个顺序节点lock1.如图接着,客户端Client1会查找locks下面的所有临时顺序子节点,判断自己的节点lock1是不是排序最小的那一个,如果是,则成功获得锁。这时候如果又来一个客户端client2前来尝试获得锁,它会在locks下再创建一个临时节点lock2客户端client2一样也会查找locks下面的所有临时顺序子节点,判断自己的节点lock2是不是最小的,此时,发现lock1才是最小的,于是获取锁失败。获取锁失败,它是不会甘心的,client2向它排序靠前的节点lock1注册Watcher事件,用来监听lock1是否存在,也就是说client2抢锁失败进入等待状态。此时,如果再来一个客户端Client3来尝试获取锁,它会在locks下再创建一个临时节点lock3
同样的,client3一样也会查找locks下面的所有临时顺序子节点,判断自己的节点lock3是不是最小的,发现自己不是最小的,就获取锁失败。它也是不会甘心的,它会向在它前面的节点lock2注册Watcher事件,以监听lock2节点是否存在。
4.2 释放锁
我们再来看看释放锁的流程,Zookeeper的客户端业务完成或者发生故障,都会删除临时节点,释放锁。如果是任务完成,Client1会显式调用删除lock1的指令如果是客户端故障了,根据临时节点得特性,lock1是会自动删除的lock1节点被删除后,Client2可开心了,因为它一直监听着lock1。lock1节点删除,Client2立刻收到通知,也会查找locks下面的所有临时顺序子节点,发下lock2是最小,就获得锁。
同理,Client2获得锁之后,Client3也对它虎视眈眈,啊哈哈~
• Zookeeper设计定位就是分布式协调,简单易用。如果获取不到锁,只需添加一个监听器即可,很适合做分布式锁。
• Zookeeper作为分布式锁也缺点:如果有很多的客户端频繁的申请加锁、释放锁,对于Zookeeper集群的压力会比较大。
5. 三种分布式锁对比
5.1 数据库分布式锁实现
优点:
• 简单,使用方便,不需要引入Redis、zookeeper等中间件。
缺点:
• 不适合高并发的场景
• db操作性能较差;
5.2 Redis分布式锁实现
优点:
• 性能好,适合高并发场景
• 较轻量级
• 有较好的框架支持,如Redisson
缺点:
• 过期时间不好控制
• 需要考虑锁被别的线程误删场景
5.3 Zookeeper分布式锁实现
缺点:
• 性能不如redis实现的分布式锁
• 比较重的分布式锁。
优点:
• 有较好的性能和可靠性
• 有封装较好的框架,如Curator
5.4 对比汇总
• 从性能角度(从高到低)Redis > Zookeeper >= 数据库;
• 从理解的难易程度角度(从低到高)数据库 > Redis > Zookeeper;
• 从实现的复杂性角度(从低到高)Zookeeper > Redis > 数据库;
• 从可靠性角度(从高到低)Zookeeper > Redis > 数据库。