IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> 分布式锁:Redisson源码解析-MultiLock、RedLock -> 正文阅读

[大数据]分布式锁:Redisson源码解析-MultiLock、RedLock

一、MultiLock

Github

我们从官方文档开始来看看 文档地址,总结一下就下面几点了:

  • Redis基于分布式的MultiLock对象,实现了一组锁对象合并成一个大锁,统一进行加锁和释放锁,每个锁对象可能属于不同的redisson实例
  • 存在一种可能,如果获取到MultiLock实例挂掉了,那么就可能导致这个multiLock一直处于被持有的状态,所以可以设置leaseTime和waitTime
  • 符合Java Lock规范,只有锁的持有者可以释放他

代码实现

// 初始化三个锁
RLock lock1 = redissonClient.getLock("lockName1");
RLock lock2 = redissonClient.getLock("lockName2");
RLock lock3 = redissonClient.getLock("lockName3");

// 初始化三个锁的合并锁
RLock multiLock = redissonClient.getMultiLock(lock1, lock2, lock3);

// 获取锁
multiLock.lock();

// 释放锁
multiLock.unlock();

二、疑问

在看完BaseLock和FairLock之后,会冒出一些需要关注的点

1. 初始化

从代码中可以看到,他是先初始化的三个RLock锁,然后再初始化的MultiLock,那么这个初始化过程做了什么呢?会有所好奇的

初始化的工作做了什么

2. 加锁

针对加锁这一块就会疑问比较多了,因为必须考虑到分布式锁的特性是如何去实现的?

锁分组

既然是批量统一的锁住一批资源,且看代码中是先获取的多个RLock,那么这些锁是如何去进行的分组呢?需要不需要分组呢?如何做到统一锁定资源的?

加锁流程

整个加锁的过程是一个什么样子的?

锁的数量

最终Redis中存留的会有几个锁呢?

猜测应该是三个,只是和有类似CountDownLatch的机制,等待三个同时获取到锁,才会说获取锁成功,加锁成功返回

可重入锁

首先,这个锁是不是可重入锁?如果是可重入锁是如何去进行控制的呢?

锁的维持

在加锁成功之后,又是怎么样来维持的这个锁呢?如果是加锁三个的话,那不是得有三个watchdog在运行?

锁互斥

锁互斥倒是好理解,可以知道如果说都获取到了锁,也就是说统一资源加锁成功,那么这些资源就都被锁到了其他线程会加锁失败

锁阻塞

有锁阻塞吗,其实每一个分组锁定的资源都是阻塞的形式

获取锁超时

如果获取锁超时,那么其他已经获取到锁的客户端,会怎么停留?直到超时了,已经获取到锁的客户端怎么办呢?

加锁失败

如何判定加锁失败,是所有的都得成功吧

3. 释放锁

被动释放

被动释放锁,应该分成几种吧

  • redisson客户端宕机了,锁被释放
  • 如果是一个客户端获取锁失败,应该也会导致其他锁的资源是放掉
  • 持有锁的时间到了,锁是如何释放的,怎么控制的

主动释放

主动释放,也就是做完了工作自己来释放

三、解析

1. 初始化

获取MultiLock的时候,实际是获取的RedissonMultiLock锁对象,接收的参数是前面初始化的RLock对象,MultiLock内部维护了一个RLock List属性

也就是说,这里的实际初始化是初始化了一个RLock列表

2. 加锁

赋值与初始化

  • waitTime:锁的获取等待时间。
    • 如果设置了-1,则表示没有等待时间,失败了就是失败了。
    • 默认调用lock方法的时候是会设置一个初始值的,baseWaitTime=locks.size() * 1500
      • 如果没有设置leaseTime,就会等于这个baseWaitTime,如果设置了lease,则根据lease的值来控制
      • 如果大于leaseTime值小于2000,则会直接设置为2000
      • 如果leaseTime的值大于2000且小于baseWaitTime,则会设置为在 baseWaitTime/2~baseWaitTime之间选取一个随机值
      • 如果leaseTime的值大于baseWaitTime,则设置为baseWaitTime~leaseTime之间的一个随机值

waitTime这样设置有什么好处呢?

  • leaseTime:锁的持有时间。
    • 默认为-1,代表可以无限时长的持有。
  • newLeaseTime:实际的锁持有时间设置,默认等于-1,如果leaseTime不等于-1,如果设置了waitTime,就会设置newLeaseTime=leaseTime × 2,否则等于leaseTime

锁的持有时间在设置了waitTime的情况下翻倍有什么含义

  • time:当前时间,每一次在循环里面获取锁之后,都需要将时间更新
  • remainTime:剩余等待时间,就等于waitTime,每一次在循环里面获取锁之后,就需要减掉(当前时间-time)
  • lockWaitTime:锁实际等待时间,会根据remainTime来计算一个锁时间等待的时间,是一个重载方法获取的数据,可能有别的算法
  • failedLocksLimit:允许获取锁失败的机器数量,也是一个重载方法,有别的用处

流程图

image.png

加锁

加锁实际上是很依赖RedissonLock的,只是做了一些算法逻辑上的控制

  1. 先将要锁定的资源放进一个list里面去维护,方便统一加锁
  2. 再遍历这个list,一个一个的加锁
  3. 实际的加锁就是走的RLock的加锁,增加了如果获取锁失败的话,对允许失败数量和超时时间、锁的持有时间进行的控制

流程图

MultiLock——加锁流程 (2).png

疑问

  1. 如果一个持有的MultiLock锁,在某个时间点,突然有一个锁的redis宕机了,这个时候怎么办?
  2. 其实想想,如果redis宕机了,就会有slave转换成master,此时这个锁如果已经同步过去了,那就没有影响了,如果没有同步过去,会出现什么呢?这样就会出现错误了,锁可能被多个客户端占有了
  3. 感觉很复杂的waitTime和leaseTime的机制?类似,leaseTime == waitTime * 2,waitTime等于lock.size * 1500

3. 释放锁

释放锁的整个流程就比较简单了,几乎是把RLock中释放锁的逻辑代码给抄过来的,就是做了一个遍历

protected RFuture<Void> unlockInnerAsync(
    					Collection<RLock> locks, long threadId) {
    if (locks.isEmpty()) {
        return RedissonPromise.newSucceededFuture(null);
    }

    RPromise<Void> result = new RedissonPromise<Void>();
    AtomicInteger counter = new AtomicInteger(locks.size());
    for (RLock lock : locks) {
        lock.unlockAsync(threadId).onComplete((res, e) -> {
            if (e != null) {
                result.tryFailure(e);
                return;
            }

            if (counter.decrementAndGet() == 0) {
                result.trySuccess(null);
            }
        });
    }
    return result;
}
  • 可以看出核心的释放锁就是:lock.unlockAsync()

疑问

  1. 什么是释放锁失败呢?失败后的表现是什么
  2. 释放失败,就是会抛出异常,返回空

四、思考解答

初始化

  • 实际初始化,就很简单,MultiLock的初始化就是将多个要统一管理的资源RLock给统一管理,初始化一个list来控制

锁分组

  • 锁分组其实是一个算法逻辑的概念,主要就是通过批量的获取RLock锁

加锁流程

  • 加锁的过程是很简单的,就是遍历调用RLock里面的方法,通过统一控制锁的超时,锁的获取成功失败数量比来控制加锁的结果

锁的数量

  • 看完整个逻辑代码,其实就很清楚了,MultiLock其实就是几个资源几个锁

可重入锁

  • 本质不能说MultiLock是可重入锁,应该说RLock是可重入锁,实际的加锁也是走的RLock的流程

锁的维持

  • 这个就更是了,和RLock是一样的,都是通过watchdog来运行的,每个锁的都会有一个watchdog

锁互斥、锁阻塞

  • 像批量锁还好一点,规定了不允许有获取失败的锁存在,如果有失败的,那么就要重头开始来
  • 只要几个被锁住的资源,还在持有锁的状态,其他锁不会再成功获取这个小锁

锁超时

  • 会维护一个remainTIme,每一次获取锁之后,都会对这个值进行递减

加锁失败

  • 里面维护了一个最小失败数量,如果失败的数量到达这么多就会直接失败掉
  • 或者是remainTime到了,超时了也会加锁失败

主动释放锁

  • 成功获取到了MultiLock,然后通过unlock来进行释放。走的是RLock的释放流程
  • 还有一种,在获取MultiLock的路上,中间有一个失败了,如果没有超时,就需要从头在来,把获取到的锁给是放掉

被动释放锁

  • 在获取MultiLock的路上,然后客户端宕机了,就会直接等待redis中的key超时了,才能加锁成功
  • 在获取到MultiLock以后,redis master宕机了,就很危险了,如果已经异步复制过去了,就不会有什么问题,如果还没复制过去,就玩完,直接多客户端加锁成功

六、RedLock

前面我们讲到,如果说master宕机的情况下,由于是异步复制的,可能存在key没有复制到slave上,就死掉了,就会导致了多客户端同时加锁成功的可能性

Github

This object is deprecated. RLock operations now propagated to all Redis slaves.

弃用了……

从使用的角度来看,是继承自MultiLock的, 重写了其中的几个方法

  • failedLocksLimit--> lock.size - (locks.size()/2 + 1)
  • calcLockWaitTime --> Math.max(remainTime / locks.size(), 1)

多节点redis实现的分布式锁算法(RedLock):有效防止单点故障

算法

假设有5个完全独立的redis主服务器

  1. 获取当前时间戳
  1. client尝试按照顺序使用相同的key,value获取所有redis服务的锁,在获取锁的过程中的获取时间比锁过期时间短很多,这是为了不要过长时间等待已经关闭的redis服务。并且试着获取下一个redis实例。
  • 比如:TTL为5s,设置获取锁最多用1s,所以如果一秒内无法获取锁,就放弃获取这个锁,从而尝试获取下个锁
  1. client通过获取所有能获取的锁后的时间减去第一步的时间,这个时间差要小于TTL时间并且至少有3个redis实例成功获取锁,才算真正的获取锁成功
  1. 如果成功获取锁,则锁的真正有效时间是 TTL减去第三步的时间差 的时间;比如:TTL 是5s,获取所有锁用了2s,则真正锁有效时间为3s(其实应该再减去时钟漂移);
  1. 如果客户端由于某些原因获取锁失败,便会开始解锁所有redis实例;因为可能已经获取了小于3个锁,必须释放,否则影响其他client获取锁
  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2021-12-06 15:20:17  更:2021-12-06 15:21:15 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:55:21-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码