分布式锁 方案 和源码总结

Redis 锁

1.最普通的锁(单机) set nx 互斥锁 设置过期时间,释放锁 直接删除key 即可,可以使用lua脚本实现

set nx中的随机值的作用: 用来判断 删除锁时 当前的锁是不是 我获取的那个锁

问题:Redis 宕机了 锁就没了

2.RedLock

这个场景是假设有一个redis cluster,有5个redis master实例。然后执行如下步骤获取一把锁:

1)获取当前时间戳,单位是毫秒

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

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

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

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

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

RedLock算法作用: 防止redis单例挂掉,获取不了锁和不能释放锁

 

redis分布式锁的方案:

1.单例   

2.主从架构 +哨兵  高可用,依然存在master宕机,锁数据还没复制到slave节点, 还是会有漏洞

3.多master集群  cluster 或者 使用twitter开源的twemproxy做客户端集群分片  :

redlock  ,每个master 都要加锁(网络等情况)可能导致每个master上的锁过期时间不一样,到时候会被其他线程获取到锁。(不够健壮并且比较复杂

4.redisson 也是比较知名的redis客户端类库,可重入锁、读写锁、公平锁、信号量、CountDownLatch,很多种复杂的锁的语义,可以支持我们将分布式锁玩儿的非常的好,

可以用 redis的Jedis + Redisson结合起来,Jedis封装redis的一些基础的语义,一些操作,都是不错的

 

 

 

 

 

 

ZK锁

zk分布式锁,其实可以做的比较简单,就是某个节点尝试创建临时znode,此时创建成功了就获取了这个锁;这个时候别的客户端来创建锁会失败,只能注册个监听器监听这个锁。释放锁就是删除这个znode,一旦释放掉就会通知客户端,然后有一个等待着的客户端就可以再次重新枷锁。

有两种实现方式:

1.全部监听锁节点,以前比较早期都是这么实现,监听的请求太多 会引起惊群效应,会并发争抢,性能比较低

2.按节点大小顺序监听排在自己前面的那个人创建的 node 上,一旦某个人释放了锁,排在自己后面的人就会被 ZooKeeper 给通知

但是,使用 zk 临时节点会存在另一个问题:由于 zk 依靠 session 定期的心跳来维持客户端,如果客户端进入长时间的 GC,可能会导致 zk 认为客户端宕机而释放锁,让其他的客户端获取锁,但是客户端在 GC 恢复后,会认为自己还持有锁,从而可能出现多个客户端同时获取到锁的情形。

 

1.zk其他没有获取到锁的线程全部监听同一个node,引起惊群效应

优化 顺序节点实现监听

 

(3)redis分布式锁和zk分布式锁的对比

 1.  性能对比:

redis分布式锁,其实需要自己不断去尝试获取锁,比较消耗性能

zk分布式锁,获取不到锁,注册个监听器即可,不需要不断主动尝试获取锁,性能开销较小

 2.释放时间对比:

另外一点就是,如果是redis获取锁的那个客户端bug了或者挂了,那么只能等待超时时间之后才能释放锁;而zk的话,因为创建的是临时znode,只要客户端挂了,znode就没了,此时就自动释放锁

3.实现机制对比:

redis分布式锁大家每发现好麻烦吗?遍历上锁,计算时间等等。。。zk的分布式锁语义清晰实现简单

所以先不分析太多的东西,就说这两点,我个人实践认为zk的分布式锁比redis的分布式锁牢靠、而且模型简单易用

 

redis 分布式锁 

 

 

重入锁源码总结

 

做一个总结,从实现原理以及源码的层面,真正剖析和了解到了redis分布式锁的企业级的实现,这个分布式锁实现的还是非常漂亮的,麻雀虽小,五脏俱全,分布式的可重入锁,总结一下redis

(1)加锁:在redis里设置hash数据结构,生存周期是30000毫秒

(2)维持加锁:代码里一直加锁,redis里的key会一直保持存活,后台每隔10秒的定时任务(watchdog)不断的检查,只要客户端还在加锁,就刷新key的生存周期为30000毫秒

(3)可重入锁:同一个线程可以多次加锁,就是在hash数据结构中将加锁次数累加1

(4)锁互斥:不同客户端,或者不同线程,尝试加锁陷入死循环等待

(5)手动释放锁:可重入锁自动递减加锁次数,全部释放锁之后删除锁key

(6)宕机自动释放锁:如果持有锁的客户端宕机了,那么此时后台的watchdog定时调度任务也没了,不会刷新锁key的生存周期,此时redis里的锁key会自动释放

(7)尝试加锁超时:在指定时间内没有成功加锁就自动退出死循环,标识本次尝试加锁失败

(8)超时锁自动释放:获取锁之后,在一定时间内没有手动释放锁,则redis里的key自动过期,自动释放锁

这8大机制,组合在一起,才是构成了一个企业级的基于redis的分布式锁的方案

redisson基于redis实现的分布式锁的核心原理给搞通透了,后续我们再看其他的锁,包括公平锁、读写锁、MultiLock、RedLock这一系列的源码的时候,就比较得心应手了,分析源码会稍微快一些,主要分析各种不同的锁之间的实现原理的区别

redis加锁,本质,还是在redis集群中挑选一个master实例来加锁,master -> slave,实现了高可用的机制,如果master宕机,slave会自动切换为master

假设客户端刚刚在master写入一个锁,此时发生了master的宕机,但是master还没来得及将那个锁key异步同步到slave,slave就切换成了新的master。此时别的客户端在新的master上也尝试获取同一个锁,会成功获取锁

此时两个客户端,都会获取同一把分布式锁,可能有的时候就会导致一些数据的问题

redisson的分布式锁,隐患主要就是在这里

 

 

FairLock 公平锁

其实就是基于重入锁的 获取锁失败的线程加入队列,排队获取锁

MultiLock 联锁

基于重入锁的基础之上 去连续获取多个锁,释放锁也是 遍历释放锁

 

RedLock 红锁 原理

 

这个场景是假设有一个redis cluster,有3个redis master实例

然后执行如下步骤获取一把分布式锁:

1)获取当前时间戳,单位是毫秒

2)跟上面类似,轮流尝试在每个master节点上创建锁,过期时间较短,一般就几十毫秒,在每个节点上创建锁的过程中,需要加一个超时时间,一般来说比如几十毫秒如果没有获取到锁就超时了,标识为获取锁失败

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

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

5)要是锁建立失败了,那么就依次删除已经创建的锁

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

他这里最最核心的一个点,普通的redis分布式锁,其实是在redis集群中根据hash算法选择一台redis实例创建一个锁就可以了

RedLock算法思想,不能只在一个redis实例上创建锁,应该是在多个redis实例上创建锁,n / 2 + 1,必须在大多数redis节点上都成功创建锁,才能算这个整体的RedLock加锁成功,避免说仅仅在一个redis实例上加锁

 

RedissonRedLock锁的实现,非常的简单,他是RedissonMultiLock的一个子类,RedLock算法的实现,是依赖于MultiLock的一个机制来实现的

重载了failedLocksLimit calcLockWaitTime 方法
failedLocksLimit ( n - (n / 2 + 1) )允许加锁失败的数量 MultiLock是不允许失败的
calcLockWaitTime ( waitTime / n ) 计算每个锁加锁的超时时间, MultiLock 是 只算总的加锁 超时时间

判断逻辑就会根据这个超时时间 和 锁失败数量去判断

这个就是MultiLock 和 RedLock的区别

 

 

zookeeper分布式锁(一):可重入锁源码剖析

 

总结:

0.节点存在就重复加锁

1.创建节点

 2.获取临时顺序节点列表

3.获取创建的节点 在列表的位置 ourIndex   
4. 是否可以获取锁判断  maxLeases(最大可以获取锁的实例)
boolean         getsTheLock = ourIndex < maxLeases;
5.获取锁成功直接返回

6.获取锁失败 获取上一个节点并且监听上一个节点删除事件
7.wait 阻塞(被唤醒后 继续while循环 尝试获取锁)
8.释放锁 锁的重入次数还大于0 就直接返回,释放锁 删除节点,删除threadData(ConcurrentMap<Thread, Node>)
9.获取锁失败的线程 被监听器 唤醒 notifyAll

 10.重新尝试获取锁

 

 

redis锁:

1.实现方案

2.架构部署 和 锁的关系

3.获取锁 释放锁 原理

4.原理图

zookeeper:

1.实现方案

2.获取锁释放锁原理

3.原理图

 

redis 和zookeeper 对比

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
redis分布式锁是一种基于redis实现的分布式锁机制,它通过redis的setnx命令来实现锁的获取和释放。在java中,我们可以通过使用redisson这个开源的redis客户端来操作redis分布式锁。 首先,我们需要在pom.xml文件中添加redisson的依赖: ```xml <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.14.0</version> </dependency> ``` 然后,在java代码中,我们可以使用redisson来获取和释放分布式锁。下面是一个简单的示例代码: ```java // 创建redisson客户端 Config config = new Config(); config.useSingleServer().setAddress("redis://127.0.0.1:6379"); RedissonClient redisson = Redisson.create(config); // 获取分布式锁 RLock lock = redisson.getLock("myLock"); try { // 尝试获取锁,最多等待10秒 boolean isLocked = lock.tryLock(10, TimeUnit.SECONDS); if (isLocked) { // 成功获取到锁,执行业务逻辑 System.out.println("成功获取到锁!"); } else { // 获取锁失败 System.out.println("获取锁超时!"); } } catch (InterruptedException e) { e.printStackTrace(); } finally { // 释放锁 lock.unlock(); } // 关闭redisson客户端 redisson.shutdown(); ``` 通过以上代码,我们可以清楚地看到如何使用redisson来实现分布式锁的获取和释放。在这个示例中,我们创建了一个名为"myLock"的分布式锁,并使用tryLock方法来尝试获取锁,最多等待10秒。获取到锁后,我们执行业务逻辑,然后通过unlock方法释放锁。在最后,记得关闭redisson客户端,释放资源。 这段代码是从Redisson官网示例中简化得到,实现了一个基本的分布式锁应用。当然,在实际项目中,我们还需要考虑一些异常情况的处理、锁的自动续期和锁的可重入性等问题。因此,在使用redis分布式锁时,需要仔细考虑各种场景下对锁的操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值