什么是Redisson分布式锁?有什么作用?

news2024/11/20 15:25:34

前言:

如果你的简历中写了做过电商项目,那么面试官基本都会从SpringBoot、SpringCloud以及Dubbo这些微服务架构涉及的知识问起,然后深入到问什么是分布事务、分布式锁以及分布式缓存等内容。

这篇文章主要聊聊什么是Redisson分布式锁,Redisson分布式锁的实现原理以及运用示例。

一、概念

Redisson是一个基于Java的分布式服务框架,提供了丰富的分布式锁实现。其底层使用了Redis作为存储介质,通过Redis的原子性操作实现了分布式锁的加锁和释放。

具体来说,Redisson的分布式锁实现分为以下几个步骤:

  • 获取分布式锁对象:使用Redisson的分布式锁API获取一个分布式锁对象。

  • 尝试加锁:调用分布式锁对象的tryLock()方法进行加锁操作。如果加锁成功,则返回true;否则返回false。

  • 执行业务逻辑:如果加锁成功,则执行相应的业务逻辑。

  • 释放锁:在业务逻辑执行完成后,调用分布式锁对象的unlock()方法释放锁

需要注意的是,为了避免死锁问题,Redisson的分布式锁还支持可重入锁和公平锁两种模式。可重入锁允许同一个线程多次获得同一个分布式锁;公平锁则按照线程请求锁的顺序来分配锁资源,避免出现饥饿现象。此外,Redisson还提供了超时机制,可以在一定时间内无响应时自动释放锁资源。

二、Redisson类型

Redisson分布式锁主要包括以下几种类型:

  1. 可重入锁(Reentrant Lock):基于CAS原子操作实现的互斥锁,线程执行时存在可重入锁的访问临界区。可重入锁可以保证同一时间只有一个线程可以访问临界区,从而保证数据的一致性。

  2. 读写锁(ReadWrite Lock):提供读写操作的互斥锁机制,允许多个线程同时读取数据,但只允许一个线程写入数据。

  3. 乐观锁(Optimistic Locking):在数据未被修改的情况下,提前返回结果;在数据发生变化的情况下,回滚事务,保证数据的一致性。乐观锁不保证锁的顺序,但是可以减少锁的释放次数,提高并发性能。

  4. 悲观锁(Pessimistic Locking):在数据未被修改的情况下,阻塞等待直到事务提交;在数据发生变化的情况下,回滚事务,保证数据的一致性。悲观锁保证了数据的正确性,但是会影响并发性能。
    不同的分布式锁在不同的场景中有不同的优缺点,开发者需要根据实际需求选择合适的锁类型。

  5. 公平锁(Fair Lock):

  6. 联锁(MultiLock):

  7. 红锁(RedLock):

  8. 信号量(Semaphore):

三、Redisson原理分析

为了更好的理解分布式锁的原理,我这边自己画张图通过这张图来分析。
在这里插入图片描述
1、加锁机制

线程去获取锁,获取成功: 执行lua脚本,保存数据到redis数据库。

线程去获取锁,获取失败: 一直通过while循环尝试获取锁,获取成功后,执行lua脚本,保存数据到redis数据库。

2、watch dog自动延期机制

最常见的使用方法

RLock lock = redisson.getLock("my-lock");
// 最常见的使用方法
lock.lock();

四、Redisson示例

1、阻塞锁

使用基本锁以后,redisson使用了自动续期,如果业务超长,运行期间自动续上30s,不用担心业务时间长,锁自动过期被删掉。

public String rlock() {

        RLock rlock = redissonClient.getLock("rlock");
        rlock.lock();//阻塞锁
        //从数据库获取
        try {
            if (rlock.isLocked()) {
                log.info("加锁成功......." + Thread.currentThread().getId());
                SpuInfoEntity spuInfo = spuInfoService.getById(11);
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                return spuInfo.getSpuName();
            }
        } finally {
            rlock.unlock();
            log.info("解锁成功......."+ Thread.currentThread().getId());
        }
        return null;
    }

2、可重入锁

仅在调用时锁是空闲的情况下才获取锁。

  • 如果锁可用,则获取锁,并立即返回值为true。
  • 如果锁不可用,那么这个方法将立即返回值为false
public String trylock() {

        RLock rlock = redissonClient.getLock("rlock");
        //从数据库获取
        try {
            boolean lock = rlock.tryLock(10, TimeUnit.SECONDS);/
            if (lock) {
                log.info("加锁成功......." + Thread.currentThread().getId());
                SpuInfoEntity spuInfo = spuInfoService.getById(11);
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                return spuInfo.getSpuName();
            }
        } catch (Exception e){

        }finally {
            rlock.unlock();
            log.info("解锁成功......."+ Thread.currentThread().getId());
        }
        return null;
    }

3、读写锁

  • 读读模式,相当于无锁
  • 写读模式:等待写锁释放
  • 写写模式:阻塞方式
  • 读写模式:有读锁,写也必须等待
@Override
    public String readlock() {
        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
        RLock rLock = readWriteLock.readLock();
        rLock.lock();
        System.out.println("读锁错过了.....");
        String writeValue;
        try {
            writeValue = redisTemplate.opsForValue().get("writeValue");
        } finally {
            rLock.unlock();
            System.out.println("读锁释放了.....");
        }
        return writeValue;
    }

    /**
     * 改数据加写锁,读数据加读锁
     * @return
     */
    @Override
    public String writelock() {

        RReadWriteLock readWriteLock = redissonClient.getReadWriteLock("rw-lock");
        RLock rLock = readWriteLock.writeLock();
        rLock.lock();
        String res = "";
        try {
            System.out.println("写锁错过了.....");
            res = UUID.randomUUID().toString();
            Thread.sleep(1000);
            redisTemplate.opsForValue().set("writeValue", res);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }finally {
            rLock.unlock();
            System.out.println("写锁释放了.....");
        }
        return res;
    }

4、闭锁

只有当countDown计算器次数大于5时, latch.await()才释放

public String lockDoor() {
        try {
            RCountDownLatch latch = redissonClient.getCountDownLatch("door");
            latch.trySetCount(5);
            latch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        return "放假了.....";
    }

    @Override
    public String gogogo(Long id) {
        RCountDownLatch latch = redissonClient.getCountDownLatch("door");
        latch.countDown(); //计数减一
        return id + "班的人走了...";
    }

5、信号量

只有park.release释放之后,park.acquire()才能获取

  @Override
   public String park() {
        try {
            redisTemplate.opsForValue().set("park", "3");
            RSemaphore park = redissonClient.getSemaphore("park");
            boolean b = park.tryAcquire();//获取一个信号量,占一个车位
            if(b){
                return "停成功了。。。";
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return "停车失败了》。。。";
    }

    @Override
    public String go(Long id) {
        try {
            RSemaphore park = redissonClient.getSemaphore("park");
            park.release(); //释放一个信号量,释放一个车位
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return id + "车走了...";
    }

五、项目Redisson配置

1、引入依赖

 <dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-data-redis</artifactId>
     <exclusions>
         <!--lettuce,redis客户端,使用netty作网络通信-->
         <exclusion>
             <groupId>io.lettuce</groupId>
             <artifactId>lettuce-core</artifactId>
         </exclusion>
     </exclusions>
 </dependency>
 <!--jedis,redis客户端,解决压测堆外内存溢出,springboot2.3.2已解决-->
 <dependency>
     <groupId>redis.clients</groupId>
     <artifactId>jedis</artifactId>
 </dependency>
 <!--redisson,redis客户端,封装了分布式锁实现,也可以使用springboot的方式,不需要自己配置-->
 <dependency>
     <groupId>org.redisson</groupId>
     <artifactId>redisson</artifactId>
     <version>3.13.3</version>
 </dependency>

2、配置RedissonClient

@Configuration
public class MyRedissonConfig {

    /**
     * 注入客户端实例对象
     */
    @Bean(destroyMethod="shutdown")
    public RedissonClient redisson(@Value("${spring.redis.host}") String host, @Value("${spring.redis.port}")String port) throws IOException {
        // 1.创建配置
        Config config = new Config();
        config.useSingleServer().setAddress("redis://" + host + ":" + port);// 单节点模式
//        config.useSingleServer().setAddress("rediss://" + host + ":" + port);// 使用安全连接
//        config.useClusterServers().addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001");// 集群模式
        // 2.创建redisson客户端实例
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }
}

3、application.yml配置

spring:
  redis:
    host: 127.0.0.1
    database: 2
    port: 6379
    timeout: 10000ms
    password:
    lettuce:
      pool:
        max-active: 5
        min-idle: 1
        max-idle: 3
        max-wait: 30000ms

六、Redisson分布式锁的缺点

Redis分布式锁会有个缺陷,就是在Redis哨兵模式下:

  • 客户端1 对某个 master节点 写入了redisson锁,此时会异步复制给对应的 slave节点。但是这个过程中一旦发生master节点宕机,主备切换,slave节点从变为了 master节点。

  • 这时客户端2来尝试加锁的时候,在新的master节点上也能加锁,此时就会导致多个客户端对同一个分布式锁完成了加锁。

  • 这时系统在业务语义上一定会出现问题, 导致各种脏数据的产生 。

  • 缺陷 在哨兵模式或者主从模式下,如果 master实例宕机的时候,可能导致多个客户端同时完成加锁。

源码下载:
https://gitee.com/charlinchenlin/koo-erp

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

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

相关文章

【Java EE 初阶】线程安全及死锁解决方案

目录 1.多线程下线程不安全的问题 1.使用多个线程对Array List集合进行添加操作并打印&#xff0c;查看结果 2.如何在多线程环境下使用线程安全的集合类 CopyOnWriteArrayList 3.多线程环境下使用队列 4.多线程环境下使用哈希表 1.HashTable线程安全 2.Concurrent Hash M…

yolov8训练记录

resume: 将model设置为最近一次训练的权重路径 (last.pt) resume设置为True YOLOv8 在 Python 环境中直接使用&#xff0c;不用 ultralytics 库 pip uninstall ultralytics 原因&#xff1a;安装 ultralytics 库&#xff0c;只能在虚拟环境中使用&#xff0c;自己修改代码非常…

ACM 1004 | 母牛的故事

文章目录 0x00 前言 0x01 题目描述 0x02 问题分析 0x03 代码设计 0x04 完整代码 0x05 运行效果 0x06 总结 0x00 前言 C 语言网不仅提供 C 语言&#xff0c;还包括 C 、 java 、算法与数据结构等课程在内的各种入门教程、视频录像、编程经验、编译器教程及软件下载、题解博…

新库上线 | CnOpenData·A股上市公司担保数据

A股上市公司担保数据 一、数据简介 “对外担保”&#xff0c;是指上市公司为他人提供的担保&#xff0c;包括上市公司对控股子公司的担保。“上市公司及其控股子公司的对外担保总额”&#xff0c;是指包括上市公司对控股子公司担保在内的上市公司对外担保总额与上市公司控股子…

初级数据结构——栈和队列

目录 1.栈栈的概念及结构栈的实现栈的结构初始化栈入栈出栈获取栈顶元素获取栈中有效元素个数检测栈是否为空销毁栈Stack.hStack.cTest.c 2.队列队列的概念及结构队列的实现队列的结构初始化队列队尾入队列队头出队列获取队列头部元素获取队列队尾元素获取队列中有效元素个数检…

【FATE联邦学习】model not init, call init_model() function

太长不看版 在local_mode下&#xff0c;不要使用t.CustModel进行实例化&#xff0c;而是直接使用原本的类进行实例化。 如果你设置了trainer.local_mode()&#xff0c;那么trainer里面的model不可以是t.CustModel()的实例。 给几个example&#xff1a; 正确&#xff1a; mod…

2023年美国大学生数学建模竞赛B题重塑马赛马拉的形象解题全过程文档及程序

2023年美国大学生数学建模竞赛 B题 重塑马赛马拉的形象 原题再现&#xff1a; 背景介绍   肯尼亚的野生动物保护区最初主要是为了保护野生动物和其他自然资源而设立的。肯尼亚议会通过了2013年《野生动物保护和管理法》&#xff0c; 以提供更公平的资源共享&#xff0c;以及…

重新定义新增长,从百果园看ESG如何可持续

当下&#xff0c;企业的增长亟待重新定义。 中国已基本告别人口红利时代&#xff0c;不少行业和企业当前陷入增长困境。原来以规模定义的增长很难实现&#xff0c;一些企业在日常运营中找不到方向和方式&#xff0c;因此需要重新定义“增长”。 最终&#xff0c;在环境恶化、…

计算机图形学-GAMES101-7

引言 场景中有很多的三角形&#xff0c;如果实现可见性和遮挡呢&#xff1f;  一个简单的想法是&#xff0c;从远到近画&#xff0c;近处的物体自然会覆盖掉远处的物体&#xff0c;这种画法也叫画家算法。  但是实际绘制中物体的顺序是不容易确定的&#xff0c;比如如下图绘制…

下载和安装appuploader

转载&#xff1a;下载和安装appuploader IOS开发工具官网地址 Appuploader home -- A tool improve ios develop efficiency such as submit ipa to appstore and manage ios certificate 最新版本已经优化了没支付688给apple的账号登录流程&#xff0c;无需再安装其他软件。…

DockerWeave容器跨机通信网络部署

Docker Weave网络部署 Weave在Docker主机之间实现Overlay网络&#xff0c;使用业界标准VXLAN封装&#xff0c;基于UDP传输&#xff0c;也可以加密传输。 Weave Net创建一个连接多个Docker主机的虚拟网络&#xff0c;类似于一个以太网交换机&#xff0c;所有的容器都连接到这上…

数组或结构体赋值时memcpy与直接赋值的效率比较

先上结论&#xff1a; 二者不一定谁快通常情况下&#xff0c;数组维度越大&#xff0c;使用memcpy效率更高数组维度越大&#xff0c;直接赋值耗时主体是循环耗时 Note&#xff1a; “等号赋值”被编译器翻译成一连串的MOV指令&#xff0c;而memcpy则是一个循环。“等号赋值”比…

05mysql---函数

目录 1:日期函数 2:字符函数 3:数值函数 4:流程函数 1:日期函数 select 函数(参数) 函数功能举例curdate()返回当前日期2023-05-17curtime()返回当前时间14:44:33now()返回当前日期和时间2023-05-17 14:44:33year(date)获取指定date的年份month(date)获取指定date的月份day…

【简介】限流

限流 为什么要限流限流算法单机限流计数器算法滑动窗口算法漏桶算法令牌桶算法 分布式限流配额算法 限流策略限流位置 为什么要限流 作为有追求的程序员&#xff0c;我们都希望自己的系统跑的飞快&#xff0c;但是速度再快&#xff0c;系统处理请求耗时也不可能为0&#xff0c…

线程池下载4K壁纸

学习记录 学习记录 一、目标 aHR0cHM6Ly9waWMubmV0Ymlhbi5jb20vDQo 拿到每张图片的href 和 标题&#xff0c;跳转到详情页进行图片下载地址的提取并请求实现图片本地下载 二、代码实现 """ CSDN: 抄代码抄错的小牛马 mailbox&#xff1a;yxhlhm2002163.com…

计算机毕业论文内容参考|基于java的房产营销系统的设计与实现

文章目录 导文摘要前言绪论课题背景课题内容相关技术与方法介绍技术分析技术设计技术实现总结与展望本文总结后续工作展望导文 计算机毕业论文内容参考|基于java的房产营销系统的设计与实现 摘要 当今社会,随着经济的快速发展和人们对生活品质的不断提高,房地产行业正在面临…

Java --- docker安装redis

目录 一、拉取redis容器镜像 二、新建目录 三、拷贝redis.conf文件模板进/app/redis目录 四、修改redis.conf文件 五、启动redis镜像服务 一、拉取redis容器镜像 docker pull redis:7.0.0 二、新建目录 mkdir -p /app/redis 三、拷贝redis.conf文件模板进/app/redis目录 cp …

Java面试(4)面向对象

文章目录 概念1. 面向对象和面向过程的区别2. 面向对象三大特征 类1. 接口和抽象类有什么共同点和区别&#xff1f;2. 深拷贝和浅拷贝区别了解吗&#xff1f;什么是引用拷贝&#xff1f;3. 内部类1. 内部类2. 内部类的优点3. 内部类有哪些应用场景4. 局部内部类和匿名内部类访问…

python 3.8 + tensorflow 2.4.0 + cuda11.0 的问题

版本匹配 &#x1f517;从源代码构建 | TensorFlow 报错&#xff1a;Could not load dynamic library ‘cupti64_110.dll’; dlerror: cupti64_110.dll not found 是因为我电脑中的 cuda 版本以前是 10&#xff0c;现在是 11.4 &#xff0c;所以需要安装对应版本的 cudatoolk…

基于粒子群算法的微网经济优化调度——附Matalb代码

目录 摘要&#xff1a; 代码主要内容&#xff1a; 研究背景&#xff1a; 微电网模型&#xff1a; 粒子群算法&#xff1a; 运行结果&#xff1a; Matlab代码分享&#xff1a; 摘要&#xff1a; 提出了一种经济与环保相协调的微电网优化调度模型&#xff0c;针对光伏电池…