Redis(9)分布式锁(2)

news2024/11/24 2:34:54

主从复制主节点宕机导致锁失效问题

主节点会不断的把自己的数据传递给从节点,来保证主节点的数据和从节点的数据是相等的, 毕竟不是在同一台机器,主和从之间会存在一定的延时,主从同步也会存在一定的延时

1)现在有一个线程1来进行从主节点上来获取锁的操作:set lock thread1 nx ex 10

2)然后主节点就会保存lock:thread1这把锁

3)然后主节点就会将这把锁的信息向同节点进行同步,但是就在此时主节点发生了故障,主从同步还没有完成主节点就宕机了,我们的redis中的哨兵会监视主从节点之间的工作状态,也就是监视集群的工作状态,如果主节点宕机了,那么首先客户端连接会断开,然后会从两个从节点中选出一个机器作为主节点;

4)然后哨兵模式随机选择一个从节点来充当主节点,但是刚才的主从同步操作没有完成就会导致所失效的问题,如果这个时候在有其他线程来尝试获取到锁,就会获取成功,从而产生线程安全问题;

解决方案:不区分主从节点,就算redis宕机了,也不会导致线程安全问题

加锁:每一个节点都保存了锁的信息才算加锁成功

 

 

@Configuration
public class RedissonConfig {
    @Bean(name="redisson1")
    public RedissonClient GetRedissonConfig(){
        //配置
        Config config=new Config();
        config.useSingleServer().setAddress("redis://124.71.136.248:6379").setPassword("12503487eA!");
         //创建RedissonCliment对象
        return Redisson.create(config);
    }
    @Bean(name="redisson2")
    public RedissonClient GetRedissonConfig2(){
        //配置
        Config config=new Config();
        config.useSingleServer().setAddress("redis://124.71.136.248:6380").setPassword("12503487eA!");
        //创建RedissonCliment对象
        return Redisson.create(config);
    }
    @Bean(name="redisson3")
    public RedissonClient GetRedissonConfig3(){
        //配置
        Config config=new Config();
        config.useSingleServer().setAddress("redis://124.71.136.248:6381").setPassword("12503487eA!");
        //创建RedissonCliment对象
        return Redisson.create(config);
    }

}

 

现在我们来查看一下RedissonMultiLock的tryLock的源码:

1)waitTime:获取到锁失败之后在此时间内会一直尝试获取到锁

2)leaseTime:过了多长时间,如果该线程还没有解锁,那么这把锁会释放

如果对应的时间传入的是-1,代表你没有传递这个释放时间,如果这个值是-1,那么代表你没有传递这个过期时间,如果你不进行传递waittime的话,说明你压根就不想重试,在获取到锁失败之后就直接返回了,因为你压根没有给他重新尝试获取锁的时间

  public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
        long newLeaseTime = -1L;
        if (leaseTime != -1L) {
            if (waitTime == -1L) {
//1.在你没有传递waitTime,此时你没有传递waittime说明你并不想重试,所以你的释放时间是多久,我就是用多久
                newLeaseTime = unit.toMillis(leaseTime);
            } else {
//2.如果你传递了waittime,说明你想要进行重试,不会用你释放的时间,那么锁过期的时间就是你等待的时间*2,因重新进行尝试可能耗时比较久,就防止万一重试时间小于等待时间,我还没有重试完呢,你就把锁释放了
                newLeaseTime = unit.toMillis(waitTime) * 2L;
            }
        }
//3.获取当前时间
        long time = System.currentTimeMillis();
//4.获取到剩余时间,初始化,remaintime就是剩余的等待时间
        long remainTime = -1L;
        if (waitTime != -1L) {
            remainTime = unit.toMillis(waitTime);
        }
//5.这是计算锁的等待时间和剩余等待时间是相同的
        long lockWaitTime = this.calcLockWaitTime(remainTime);
//6.failedlocksLimit是剩余失败的锁的限制,在源码里面默认是0
        int failedLocksLimit = this.failedLocksLimit();
//7. acquiredLocks代表已经获取到的锁的个数,表示加锁成功的个数
        List<RLock> acquiredLocks = new ArrayList(this.locks.size());
//8.遍历到三个独立的锁
        ListIterator iterator = this.locks.listIterator();

        while(iterator.hasNext()) {
            RLock lock = (RLock)iterator.next();

            boolean lockAcquired;
            try {
                if (waitTime == -1L && leaseTime == -1L) {
//如果你不进行传递waittime表示只是进行重新尝试一次
                    lockAcquired = lock.tryLock();
                } else {
//表示你想进行尝试多次lockAcquired代表返回值,表示获取锁成功还是获取锁失败
                    long awaitTime = Math.min(lockWaitTime, remainTime);
                    lockAcquired = lock.tryLock(awaitTime, newLeaseTime, TimeUnit.MILLISECONDS);
                }
            } catch (RedisResponseTimeoutException var21) {
                this.unlockInner(Arrays.asList(lock));
                lockAcquired = false;
            } catch (Exception var22) {
                lockAcquired = false;
            }

            if (lockAcquired) {
//1.如果获取锁成功了,那么把它加入到acquiredLocks集合中,表示获取锁成功的集合
                acquiredLocks.add(lock);
            } else {
//2.当获取到某一把锁的时候获取失败了,判断锁得总的数量-获取到锁成功的数量==0,失败的上限
failedLocksLimit(),也就是说只有把所有的锁都获取到了,这个条件才成立
                if (this.locks.size() - acquiredLocks.size() == this.failedLocksLimit()) 
               {
                    break;
                }
//3.判断failedLocksLimit == 0==0
                if (failedLocksLimit == 0) {
//释放所有已经尝试获取成功的锁
                    this.unlockInner(acquiredLocks);
                    if (waitTime == -1L) {
//如果waittime为0,那么根本没有重试的机会,直接返回false
                        return false;
                    }
//4代码走到这里面,说明程序是想要进行重试操作的,先把已经获取成功的锁进行清空
                    failedLocksLimit = this.failedLocksLimit();
                    acquiredLocks.clear();
//5.将迭代器向前进行遍历,再次尝试获取到锁
                    while(iterator.hasPrevious()) {
                        iterator.previous();
                    }
                } else {
                    --failedLocksLimit;
                }
            }

            if (remainTime != -1L) {
//判断剩余的可以重新尝试获取锁的时间锁的时间
                remainTime -= System.currentTimeMillis() - time;
                time = System.currentTimeMillis();
//如果剩余的可以重新尝试获取锁的时间已经小于0了,那么说明在之前尝试获取到锁的时候就已经超时了,那么程序就会让已经尝试获取到的成功的锁全部释放掉
                if (remainTime <= 0L) {
//释放所有已经曾经尝试获取成功的锁
                    this.unlockInner(acquiredLocks);
                    return false;
                }
            }
        }
//6.程序会进行判断当前锁的施放时间如果我们自己指定了
        if (leaseTime != -1L) {
            List<RFuture<Boolean>> futures = new ArrayList(acquiredLocks.size());
            Iterator var24 = acquiredLocks.iterator();
//7.如果程序已经成功地进入到这个循环,那么会使用迭代器来继续进行遍历,重新来给每一把锁expireAsync重新设置有效期,因为我们在尝试获取到每一把锁的时候,都会消耗一定的时间,如果程序是先进行获取到的锁1,那么锁1就立即开始进行倒计时的操作,此时程序还没来得及获取锁2呢,这显然是不符合逻辑的,只有说把三个锁都获取时间之后,把他们的时间进行重置,才能保证三把锁同时释放,防止有的锁释放了,有的锁没释放
//8.如果没有设置realsetime的时间,那么我们直接会触发看门狗机制,就永远不过期了
            while(var24.hasNext()) {
                RLock rLock = (RLock)var24.next();
                RFuture<Boolean> future = ((RedissonLock)rLock).expireAsync(unit.toMillis(leaseTime), TimeUnit.MILLISECONDS);
                futures.add(future);
            }

            var24 = futures.iterator();

            while(var24.hasNext()) {
                RFuture<Boolean> rFuture = (RFuture)var24.next();
                rFuture.syncUninterruptibly();
            }
        }

        return true;
    }

分布式锁总结:

一)不可重入的redis分布式锁:

最早的锁,是利用setnx的互斥性,利用ex避免死锁,释放锁的时候判断线程标识避免误删

缺陷:是一个不可重入锁,无法进行重试,并且锁超时可能会有一些自动释放的风险

二)可重入的redis分布式锁:

原理:使用hash结构来记录线程标识和锁的重入次数,利用watchDong来自动延序锁的超时释放时间,确保不会因为业务执行时间过长而导致所自动释放,除非是服务器宕机,利用信号量控制锁重试等待,利用发布订阅这种方式来进行控制锁的等待时间,从而减少了锁重试的次数,避免浪费大量CPU资源,对于CPU利用率比较高

缺点:redis宕机导致所失效的问题,引起主从的一致性的问题

三)Redisson中的multiLock:

原理:多个独立的Redis节点,节点之间没有主从关系,就不会因为主从一致导致锁失效了,必须在所有节点都获取到这个可重入锁才算成功

缺点:至少建立三个独立节点,也可以给每一个独立节点建立多个主从

优惠卷优化: 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/518937.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

人工智能引发了科学研究的革命

人工智能引发了科学研究的革命 科学研究从第一&#xff0c;第二范式&#xff0c;升级到第三范式 趣讲大白话&#xff1a;人工智能成精了 【趣讲信息科技162期】 **************************** 国内顶尖的AI专家陆奇总结 科学研究的五个范式 1、经验主义&#xff08;比如中医&am…

Windows10安装二进制Mysql-5.7.41

1.创建my.ini [mysqld] ##skip-grant-tables1 port 3306 basedirD:/webStudy/mysql-5.7.41 datadirE:/adata/mysqlData max_connections200 character-set-serverutf8 default-storage-engineINNODB sql_modeNO_ENGINE_SUBSTITUTION,STRICT_TRANS_TABLES [mysql] default-char…

软件测试面试题【2023最新合集】

收集了各大公司的面试经验&#xff0c;现整理出来&#xff0c;希望能给正在找工作的志同道合的小伙伴一些指引&#xff0c;本文会持续更新的哦。 1、 CPU 和 GPU的区别 一个是通用计算&#xff0c;一个是专用计算。 CPU主要负责操作系统和应用程序&#xff0c;GPU主要负责…

LDAP配置与安装

LDAP配置与安装 一、安装LDAP1、安装OpenLDAP及相关依赖包2、查看OpenLDAP版本3、配置OpenLDAP数据库4、设置OpenLDAP的管理员密码5、修改配置文件5.1. 修改{2}hdb.ldif文件5.2. 修改{1}monitor.ldif文件5.3. 修改{-1}frontend.ldif文件 6、验证LDAP的基本配置7、修改LDAP文件权…

python-39-异步aiohttp爬取数据

Python分别用单线程&#xff0c;多线程&#xff0c;异步协程爬取一部小说&#xff0c;最快仅需要5s Python异步爬虫之协程抓取妹子图片(aiohttp、aiofiles) Python爬虫——教你异步爬虫二十秒爬完两百多万字六百多章的小说 python爬虫-异步爬虫 用python批量把小说编号由大写数…

RVO的寻路障碍物改进

先说下原理&#xff1a;RVO的障碍识别是通过链接的坐标点数组围成的区域&#xff0c;收尾相接形成的。 namespace RVO { /** * <summary>Defines static obstacles in the simulation.</summary> * 障碍区的点坐标数据结构&#xff0c;是个双向链表结构 *通过Sim…

【Java】认识String类

【Java】认识String类 1. String类的重要性2. 常用方法2.1 字符串构造求字符串长度2.2 String对象的比较2.3 字符串查找2.4 转化2.5 字符串替换2.6 字符串截取2.7 字符串拆分2.8 其他操作方法2.8.1 String trim()2.8.2 String toUpperCase()2.8.3 String.format&#xff08;&am…

在C#WinForm中调用julia函数及在C#中传递参数到julia函数的使用示例

特别声明&#xff1a;未经允许&#xff0c;请勿转载&#xff01; https://discourse.juliacn.com/t/topic/7189https://discourse.juliacn.com/t/topic/7189我在julia中国社区已提交了文章的最后部分未解决问题&#xff0c;大家后续可以在该链接中跟踪问题的回答进度。 好几…

Neck网络 FPN + PAN 改进解读

呃 这篇文章的目的在于补充一些知识以便于理解Neck部分的网络 特征提取网络 与 目标检测之间的关系 一个特征提取网络&#xff0c;假设有1000层&#xff0c;开始的特征图包含的细节信息就很多&#xff0c;而随着网络的加深&#xff0c;特征提取网络经过多次被卷积和池化操作&…

让你的SQL变得更简洁:学会ChatGPT活用技巧,轻松实现SQL格式化

文章目录 前言让你的SQL变得更简洁&#xff01;学会ChatGPT活用技巧&#xff0c;轻松实现SQL格式化ChatGPT格式化SQL效果讯飞星火认知大模型格式化SQL效果文心一言格式化SQL效果格式化SQL效果结果比较 总结 【免责声明】文章仅供学习交流&#xff0c;观点代表个人&#xff0c;与…

Vue--》如何在Vue3中书写TSX

在vue2的时候就已经可以使用 jsx 语法&#xff0c;但是不是很友好&#xff0c;写起来是一件很痛苦的事情&#xff0c;所以你很少见到有人会在vue2中书写 jsx 语法&#xff0c;官方也不建议我们在vue2中进行书写 jsx 的代码风格&#xff1a; 但随着vue3版本的到来&#xff0c;对…

SBUS协议解析图解

SBUS协议在硬件上是串口反相协议。 在软件上就是普通串口协议&#xff0c;波特率100k&#xff0c;8位数据位&#xff0c;2位停止位&#xff0c;偶校验even。注意&#xff0c;stm32把校验位也当做数据&#xff0c;所以数据位要设置成9&#xff0c;而不是8。 其报文格式为&…

scala---spark本地调式远程获取hdfs数据注意事项

文章目录 前言一、Hadoop配置注意事项1.1 core-site.xml1.2 core-site.xml 二、本地hadoop环境配置注意事项三、本地scala项目spark代码调试总结 前言 这篇文章主要帮大家绕开一些本地使用spark调试获取远程hdfs数据的坑&#xff0c;个人在使用时也是基本把这些坑踩了一遍。希望…

永远不该忘记!科技才是硬道理,手中没有剑,跟有剑不用,是两回事

今天是全国防灾减灾日&#xff0c;距离2008年汶川大地震也已经过去15年了。但时至今日&#xff0c;看到那些图像视频资料&#xff0c;那种触及灵魂的疼痛仍是存在的&#xff0c;2008年的大地震在每个中国人身上都留下了无法抚平的伤疤。 2008年是所有中国人都无法忘记的一年&am…

设计模式——单一职责模式之装饰模式

文章目录 前言一、“单一职责” 模式二、Decorator 装饰模式1、动机2、模式定义3、伪代码示例①、第一种写法②、第二种写法③、第三种写法 4、结构 总结 前言 一、“单一职责” 模式 在软件组件的设计中&#xff0c;如果责任划分的不清晰&#xff0c;使用继承得到的结果往往是…

深度指南:WhatsApp广播vs WhatsApp群组(二)

如何为创建WhatsApp群组&#xff1f; 建立 WhatsApp 群组&#xff0c;请按下列步骤操作。 1. 导航到您的WhatsApp或WhatsApp for Business&#xff08;布局或界面相同&#xff09;&#xff0c;然后点击“新建组”。 2.现在&#xff0c;您可以将参与者添加到您的WhatsApp群组…

华为OD机试真题 Java 实现【静态代码扫描服务】【2023Q1 100分】

一、题目描述 静态扫描快速识别源代码的缺陷&#xff0c;静态扫描的结果以扫描报告作为输出&#xff1a; 文件扫描的成本和文件大小相关&#xff0c;如果文件大小为N&#xff0c;则扫描成本为N个金币&#xff1b;扫描报告的缓存成本和文件大小无关&#xff0c;每缓存一个报告…

算法修炼之练气篇——练气十层

博主&#xff1a;命运之光 专栏&#xff1a;算法修炼之练气篇 前言&#xff1a;每天练习五道题&#xff0c;炼气篇大概会练习200道题左右&#xff0c;题目有C语言网上的题&#xff0c;也有洛谷上面的题&#xff0c;题目简单适合新手入门。&#xff08;代码都是命运之光自己写的…

华为网络篇 静态路由与BFD联动-21

实验难度2实验复杂度2 目录 一、实验原理 二、实验拓扑 三、实验步骤 四、实验过程 总结 一、实验原理 BFD&#xff08;Bidirectional Forwarding Detection&#xff0c;双向转发检测&#xff09;是一种实验网络可靠性的机制&#xff0c;它可用于快速检测网络中的链路状况…

常见自动化测试工具及框架有哪些?怎么选?

目录 一、自动化测试简介 1、什么是自动化测试 2、它可以做自动化测试么 二、工具篇 1、UFT&#xff08;QTP&#xff09; 2、Selenium 3、Appium 4、工具对比 三、框架篇 1、TestNG 2、unittest 3、pytest 4、Robot Framework 5、框架对比 四、如何选择 总结 一…