分布式锁Redis和ZK实现异同

Published: 17 Apr 2020 Category: redis

一、结论先行

redis分布式锁,其实需要自己不断去尝试获取锁,比较消耗客户端性能; 如果是redis获取锁的那个客户端bug了或者挂了,那么只能等待超时时间之后才能释放锁。

zk分布式锁

  • 获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,(客户端)性能开销较小。
  • 可以避免服务宕机导致的锁无法释放,而产生的死锁问题。
  • 每次在创建锁和释放锁的过程中,都要动态创建、销毁瞬时节点来实现锁功能。ZK中创建和删除节点只能通过Leader服务器来执行,然后将数据同不到所有的Follower机器上。(获取或释放锁的性能较redis差)

二、实现原理

2.1 Redis分布式锁

实现方式一: SET lockKey random_value NX EX 30

根据lockKey区进行setnx(set not exist,如果key值为空,则正常设置,返回1,否则不会进行设置并返回0)操作,如果设置成功,表示已经获得锁,否则并没有获取锁。

释放锁就是删除key,但是一般可以用lua脚本删除,判断value一样才删除。

Q: 为什么要设置过期时间?
A: 因为如果要释放锁的客户端因为断电或网络原因,没有及时释放锁,那么这个锁会一直存在,其他客户端就永远都拿不到锁。

Q: 为啥key对应的value要用随机值呢?
A: 避免释放了别的客户端申请的锁。在设置key的时候,将value设置为一个随机值r,当释放锁,也就是删除key的时候,不是直接删除,而是先判断该key对应的value是否等于先前设置的随机值,只有当两者相等的时候才删除该key,由于每个客户端产生的随机值是不一样的,这样一来就不会误释放别的客户端申请的锁了。

Q: 上述方案在集群环境会有什么问题? A: 如果是redis普通主从,那redis主从异步复制,如果主节点挂了,key还没同步到从节点,此时从节点切换为主节点,别人就会拿到锁。

如果是redis普通主从,那redis主从异步复制,如果主节点挂了,key还没同步到从节点,此时从节点切换为主节点,别人就会拿到锁。

实现方式二:RedLock算法

该算法可以应对集群情况下加锁问题。(但是对于普通的单master主从架构,仍旧会存在重复拿锁的问题)

跟上面类似,轮流尝试在每个master节点上创建锁,过期时间较短,一般就几十毫秒

尝试在大多数节点上建立一个锁,比如5个节点就要求是3个节点(n / 2 +1)

客户端计算建立好锁的时间,如果建立锁的时间小于超时时间,就算建立成功了

要是锁建立失败了,那么就依次删除这个锁

只要别人建立了一把分布式锁,你就得不断轮询去尝试获取锁

pic

2.2 Zookeepper分布式锁

基于临时顺序节点:

1.客户端调用create()方法创建名为“locknode/guid-lock-”的节点,需要注意的是,这里节点的创建类型需要设置为EPHEMERAL_SEQUENTIAL。

2.客户端调用getChildren(“locknode”)方法来获取所有已经创建的子节点。

3.客户端获取到所有子节点path之后,如果发现自己在步骤1中创建的节点是所有节点中序号最小的,那么就认为这个客户端获得了锁。

4.如果创建的节点不是所有节点中序号最小的,那么则监视比自己创建节点的序列号小的最大的节点,进入等待。直到下次监视的子节点变更的时候,再进行子节点的获取,判断是否获取锁。

释放锁的过程相对比较简单,就是删除自己创建的那个子节点即可。

Zookeeper是一个保证了弱一致性即最终一致性的分布式组件。

REF

Redis与Zookeeper实现分布式锁的区别