分布式锁(Redis,zk,db锁)

  |   0 评论   |   1,279 浏览

关于分布式锁,一般有三种选择,

1、redis (setnx,redisson,redlock)

2、zk

3、DB锁(悲观锁、乐观锁)

其中用的最多的应该是redis。

redis常用的方式有单节点、主从模式、哨兵模式、集群模式。

单节点在生产环境基本上不会使用,因为不能达到高可用,且连RDB或AOF备份都只能放在master上,所以基本上不会使用。

另外几种模式都无法避免两个问题:

1、异步数据丢失。

2、脑裂问题。

setnx

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

2.如果没有获得锁,去Redis上拿到该key对应的值,在该key上我们存储一个时间戳(用毫秒表示,t1),为了避免死锁以及其他客户端占用该锁超过一定时间(5秒),使用该客户端当前时间戳,与存储的时间戳作比较。

3.如果没有超过该key的使用时限,返回false,表示其他人正在占用该key,不能强制使用;如果已经超过时限,那我们就可以进行解锁,使用我们的时间戳来代替该字段的值。

4.但是如果在setnx失败后,get该值却无法拿到该字段时,说明操作之前该锁已经被释放,这个时候,最好的办法就是重新执行一遍setnx方法来获取其值以获得该锁。

5.释放锁:删除redis中key

redlock 红锁

所以redis官方针对这种情况提出了红锁(Redlock)的概念。

假设有5个redis节点,这些节点之间既没有主从,也没有集群关系。客户端用相同的key和随机值在5个节点上请求锁,请求锁的超时时间应小于锁自动释放时间。当在3个(超过半数)redis上请求到锁的时候,才算是真正获取到了锁。如果没有获取到锁,则把部分已锁的redis释放掉。

redisson

Redisson是java的redis客户端之一,提供了一些api方便操作redis。
官网api
redisson普通的锁实现源码主要是RedissonLock这个类.
源码中加锁/释放锁操作都是用lua脚本完成的,封装的非常完善,开箱即用。

zk分布式锁

1.基于临时顺序节点:

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

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

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

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

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

2.基于创建临时节点:
1.就是某个节点尝试创建临时znode,此时创建成功了就获取了这个锁;这个时候别的客户端来创建锁会失败,只能注册个监听器监听这个锁。

2.释放锁就是删除这个znode,一旦释放掉就会通知客户端,然后有一个等待着的客户端就可以再次重新加锁。

  • 总结:
  1. redis 需要轮询比较消耗性能,而zk是主动通知,因此zk效率更高;
  2. redis 创建锁的客户端如果挂了,只能等待超时后解锁;而zk在客户端挂了后就会自动释放锁;
  3. redis 需要做值判断,写脚本等,而且redlock也会存在多节点数据问题,比较麻烦;zk的语义简单明了。
    各有利弊。看使用场景去区分使用把
    虽然redis那么多问题。但我就是喜欢使用redis锁。

参考:https://zhuanlan.zhihu.com/p/111329801
参考:https://www.cnblogs.com/mengchunchen/p/9647756.html


标题:分布式锁(Redis,zk,db锁)
作者:jackssybin
地址:https://jackssybin.cn/articles/2020/04/11/1586608404790.html

评论

发表评论


取消