面试笔记——Redis(分布式锁的使用场景及实现原理)

news2024/11/13 10:50:56

分布式锁的使用场景

  1. 资源竞争控制:多个客户端同时访问共享资源时,可以使用分布式锁来控制资源的并发访问,防止多个客户端同时对同一资源进行修改造成数据不一致的问题。

  2. 避免重复操作:在分布式环境中,可能会出现多个客户端同时执行某个操作,为了避免重复执行某个操作,可以使用分布式锁来确保只有一个客户端能够执行操作,其他客户端需要等待。

  3. 限流控制:可以使用分布式锁来限制某个操作的并发执行数量,从而避免系统过载或资源耗尽的情况。

抢券场景

	/**
     * 抢购优惠券
     * @throws InterruptedException
     */
    public void rushToPurchase() throws InterruptedException {
    //获取优惠券数量
    Integer num = (Integer) redisTemplate.opsForValue().get(“num”);
    //判断是否抢完
    if (null == num || num <= 0) {
    throw new RuntimeException(“优惠券已抢完");
    }
    //优惠券数量减一,说明抢到了优惠券
    num = num - 1;
    //重新设置优惠券的数量
    redisTemplate.opsForValue().set("num", num);
    }

在这里插入图片描述
正常的执行情况:
在这里插入图片描述
由于线程可以是交替执行的,所以可能出现以下情况——导致超出库存数量:
在这里插入图片描述
使用synchronized(属于JVM的本地锁)对资源进行加锁:

    public void rushToPurchase() throws InterruptedException {
        synchronized (this) {
            //查询优惠券数量
            Integer num = (Integer) redisTemplate.opsForValue().get("num");
            //判断是否抢完
            if (null == num || num <= 0) {
                throw new RuntimeException("商品已抢完");
            }
        
            //优惠券数量减一(减库存)
            num = num - 1;
            //重新设置优惠券的数量
            redisTemplate.opsForValue().set("num", num);
        }
    }

针对单体项目,并且只启动了一台服务,则该方式是有效的,运行过程如下图所示:
在这里插入图片描述
但是在实际项目中,为了支持并发请求,会把服务做成集群部署——同一份代码部署在多台服务器上,如图:
在这里插入图片描述
因此,可能会出现如下情况:
在这里插入图片描述
出现以上情况,是因为我们添加的synchronized,属于JAM的本地锁,每个服务器都有各自的JVM,它只能解决同一个JVM下的线程互斥。
采用分布式锁:
在这里插入图片描述

上图的流程为:首先,8080的线程1尝试获取分布锁,获取锁后,分布式锁中会添加记录“线程1”,证明该线程已经持有锁了,随后执行自己的业务代码;若8081的线程1想要获取分布式锁,此时就会失败,会进入阻塞状态;当8080的线程1执行结束后,8081的线程1才能获取分布式锁,然后执行自己的业务逻辑。

Redis分布式锁

Redis实现分布式锁主要利用Redis的setnx命令。setnx是SET if not exists(如果不存在,则 SET)的简写。

  • 获取锁
    # 添加锁
    # lock表示锁(key)的名称
    # value表示key的值
    # NX是互斥
    # EX是设置超时时间
    # 注意:可以用两条语句分别设置NX和EX(两条命令不能保证原子性);但写在一条命令中执行,可以保证原子性
    # 若不设置过期时间,可能会出现死锁问题,分析如下图所示。
    SET lock value NX EX 10 
    
  • 释放锁
    # 释放锁,删除即可
    DEL key
    

在这里插入图片描述
上图中,某一个成功获取锁之后,在执行代码的过程中发生了业务超时或服务宕机,若未设置过期时间,则分布式锁就无法被释放,其他进程就一直不能获取到资源,发生死锁现象。

由上引申出——Redis实现分布式锁如何合理的控制锁的有效时长?(即,出现到达设置的超时时间时,业务还没执行结束)
答:根据业务执行时间预估(但是该方式不太靠谱,万一出现抖动或网络卡顿,都会导致执行时间变慢,这个时间是不好控制的)或 采取给锁续期(创建一个新的线程用于监控,若业务执行时间较长时,就增加这个当前业务线程持有锁的时长——通过redisson实现)。

redisson实现的分布式锁——执行流程
在这里插入图片描述
redisson的使用代码:
在这里插入图片描述
注意:若设置了第2个参数——当前锁自动释放时间,就不会有watch dog的监听了,因为redission任务如果能够控制锁的失效时间,就没有做续期的必要了;若设置为-1或不设置值,才有监听。
lua脚本的最大作用——能够调用redisson命令来保证多条命令执行的原子性。

redisson实现的分布式锁——可重入
可重入: 指的是可以允许同一个线程多次获取同一个锁而不会造成死锁的情况。

	public void add1() {
        RLock lock = redissonClient.getLock("templock");
        boolean isLock = lock.tryLock();
        //执行业务
        add2();
        //释放锁  
        lock.unlock();
    }

    public void add2() {
        RLock lock = redissonClient.getLock("templock");
        boolean isLock = lock.tryLock();
        //执行业务  
        // 释放锁  
        lock.unlock();
    }

根据上述代码执行的情况,若add2()中的锁获取成功,则说明该锁是可重入的;若未获取成功,则说明该锁是不可重入的。
redisson实现的分布式锁是可重入的,它是根据线程ID来进行判断的。
可重入分布式锁的实现方式一般是通过在锁的持有信息中记录当前线程的标识以及获取锁的次数当同一个线程再次获取锁时,会增加锁的获取次数,而释放锁时则会递减锁的获取次数。只有当锁的获取次数减到零时,锁才会被完全释放。因此,当上述代码中,当add2()获取锁时(获取成功后,还未释放),redisson利用hash结构来记录锁的使用情况如下:
在这里插入图片描述
可重入的好处:

  1. 避免死锁:如果一个线程已经持有了锁,再次获取同一把锁时不会被阻塞,这样可以避免因为锁的重复获取而造成死锁的情况。

  2. 简化代码逻辑:在需要多层嵌套调用的情况下,使用可重入锁可以避免由于嵌套调用而引入额外的锁管理逻辑。线程只需要在最外层获取锁,在需要获取锁的内部方法中可以直接调用锁的方法,而不需要额外考虑锁的释放和获取。

  3. 提高灵活性:可重入锁允许同一个线程在持有锁的情况下多次获取锁,这意味着线程可以更加灵活地管理锁的获取和释放,从而更好地适应复杂的业务逻辑。

  4. 减少资源竞争:在一些需要频繁获取锁的场景下,可重入锁可以减少因为频繁获取锁而造成的资源竞争,从而提高系统的性能和吞吐量。

redisson实现的分布式锁-主从一致性:
主从集群架构是一种常见的分布式架构模式,通常用于构建高可用性和容错性的系统。在主从集群架构中,系统由一个主节点多个从节点组成,主节点负责处理客户端的读写请求,而从节点则负责复制主节点的数据,以提供数据备份和故障恢复能力。主节点主要负责写操作,如增、删、改;从节点主要负责对外的读操作。当主节点的数据发生改变之后,就会把数据同步给从节点。
注意的是:redisson锁不能解决数据主从一致的问题。
主从不一致的情况:下图中,Java应用创建了一个分布式锁,把数据写入主节点中,然后由主节点同步数据到各个从节点:
在这里插入图片描述
当主节点在写入数据后,此时新的数据并未同步到从节点中,若此时主节点发生了宕机,就会在从结点中选择一个结点作为新的主节点,如下图所示:
在这里插入图片描述
此时,若又有一个新的应用获取到分布式锁,则出现了两个线程同时持有同一把锁的情况,丧失了锁的互斥性,有可能出现脏数据的现象。
为了避免以上情况,采用RedLock算法来保证主从一致性。RedLock 算法的核心思想是通过在多个 Redis 节点上获取分布式锁,并确保大多数节点都成功获取锁才认为锁获取成功,如图所示:
在这里插入图片描述
但在实际应用中,并不怎么采取RedLock方式(并且官方也不建议直接使用RedLock来解决主从一致的问题),因为需要提供多个独立redis实例,导致在高并发业务中性能很差,并且实现负责、运维繁琐。
Redis的AP思想——保证高可用性;zookeeper的CP思想——保证数据的强一致性。

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

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

相关文章

rmvb是什么文件格式?rmvb格式怎么改成mp4?

RMVB&#xff0c;全称RealMedia Variable Bitrate&#xff0c;是由RealNetworks公司开发的一种视频文件格式。其产生背景可追溯至上世纪90年代&#xff0c;为了解决传输和存储上的挑战&#xff0c;RealNetworks公司致力于推出一种更为高效的解决方案。于是&#xff0c;RMVB问世…

还在用传统知识库?AI知识库才是企业的最优选择

在数字化和信息化日趋严重的时代&#xff0c;企业不仅要处理海量的数据&#xff0c;同时还要有效地管理和利用它们。这就使得知识库&#xff0c;作为一种集中存储、管理和共享知识资源的工具&#xff0c;被越来越多的企业所重视。然而&#xff0c;随着技术的快速迭代&#xff0…

Django之Celery篇(一)

一、介绍 Celery是由Python开发、简单、灵活、可靠的分布式任务队列,是一个处理异步任务的框架,其本质是生产者消费者模型,生产者发送任务到消息队列,消费者负责处理任务。 Celery侧重于实时操作,但对调度支持也很好,其每天可以处理数以百万计的任务。特点: 简单:熟悉…

ElasticSearch8 - 基本操作

前言 本文记录 ES 的一些基本操作&#xff0c;就是对官方文档的一些整理&#xff0c;按自己的习惯重新排版&#xff0c;凑合着看。官方的更详细&#xff0c;建议看官方的。 下文以 books 为索引名举例。 新增 添加单个文档 (没有索引会自动创建) POST books/_doc {"n…

消息队列八股

RabbitMQ 确保消息不丢失 重复消费问题 延迟队列 消息堆积 高可用 很少使用 Kafka 如何保证消息不丢失 回调接口保证生产者发送到brocker消息不丢失 保证消息顺序性 高可用机制 数据清理机制 实现高性能的设计

mysql - 缓存

缓存 InnoDB存储引擎在处理客户端的请求时&#xff0c;当需要访问某个页的数据时&#xff0c;就会把完整的页的数据全部加载到内存中&#xff0c;也就是说即使我们只需要访问一个页的一条记录&#xff0c;那也需要先把整个页的数据加载到内存中。将整个页加载到内存中后就可以…

学习vue3第十一节(依赖注入:provide/inject)

本机介绍&#xff1a;provide/inject 注意&#xff1a;大家在看此小节时候&#xff0c;默认大家已经了解一些组件的使用方法 1、依赖注入的用途&#xff1a; 当嵌套层级多的时候&#xff0c;某个子组件需要较远层级的父组件数据时候&#xff0c;如果我们依然使用props 传递数…

virtualbox导入vdi

新建虚拟机 点击新建 输入新建属性 配置cpu和内存 虚拟硬盘 这里选择已有的vdi文件 摘要 这里点击完成 虚拟机添加成功 点击启动&#xff0c;启动虚拟机 注意 这个时候的ip&#xff0c;还是以前镜像的ip&#xff0c;如果两个镜像一起启动&#xff0c;则需要修 改ip地…

802.1X网络访问控制协议

802.1X是一种由IEEE&#xff08;电气和电子工程师协会&#xff09;制定的网络访问控制协议&#xff0c;主要用于以太网和无线局域网&#xff08;WLAN&#xff09;中基于端口的网络接入控制。802.1X协议通过认证和授权机制&#xff0c;确保只有合法的用户和设备才能够接入网络&a…

Java中static、final关键字【详解】

文章目录 一、static关键字1.1 成员变量1.1.1 静态变量及其访问1.1.2 实例变量及其访问 1.2 成员方法1.2.1 静态方法及其访问1.2.2 实例方法及其访问 1.3 小结1.4 static应用知识 二、final关键字2.1 修饰类&#xff1a;不能被继承2.2 修饰方法2.3 修饰变量-局部变量2.3.1 局部…

背景减除(1)--bgslibrary Windows编译和使用

入侵监控领域中&#xff0c;在固定场景下&#xff0c;需要检测和监控的入侵物体种类繁多&#xff0c;无法具体穷尽。传统的CV算法提取的特征应用场景有限&#xff0c;无法完成大量物体的监控&#xff1b;深度学习目标检测方法没法收集到无穷无尽的物体种类&#xff0c;因此监督…

水牛社五大赚钱栏目概览:轻松了解项目核心与赚钱原理

很多新用户首次访问水牛社官网时&#xff0c;可能会感到有些迷茫。由于软件介绍相对较长&#xff0c;部分朋友可能缺乏耐心细读。然而&#xff0c;若您真心希望在网络上找到赚钱的机会&#xff0c;深入了解我们的发展历程将大有裨益。简而言之&#xff0c;本文旨在快速带您领略…

大数据基础:Linux基础详解

课程介绍 本课程主要通过对linux基础课程的详细讲解&#xff0c;让大家熟练虚拟机的安装使用&#xff0c;Linux系统的安装配置&#xff0c;学习掌握linux系统常用命令的使用&#xff0c;常用的软件安装方法&#xff0c;制作快照&#xff0c;克隆&#xff0c;完成免密登录&…

深度学习图像处理02:Tensor数据类型

上一讲深度学习图像处理01&#xff1a;图像的本质&#xff0c;我们了解到图像处理的本质是对矩阵的操作。这一讲&#xff0c;我们讲介绍深度学习图像处理的基本数据类型&#xff1a;Tensor类型。 在深度学习领域&#xff0c;Tensor是一种核心的数据结构&#xff0c;用于表示和…

蓝桥杯单片机快速开发笔记——特训2 按键的长按与短按

一、题目要求 在CT107D单片机综合训练平台上&#xff0c;通过I/O模式编写代码&#xff0c;实现以下功能&#xff1a; 系统上电后&#xff0c;关闭蜂鸣器、继电器和全部指示灯&#xff0c;数码管显示初始值为28&#xff0c;仅显示数码管最右边两位。利用定时器0实现10ms间隔定…

代码随想录第20天| 654.最大二叉树 617.合并二叉树

654.最大二叉树 654. 最大二叉树 - 力扣&#xff08;LeetCode&#xff09; 代码随想录 (programmercarl.com) 又是构造二叉树&#xff0c;又有很多坑&#xff01;| LeetCode&#xff1a;654.最大二叉树_哔哩哔哩_bilibili 给定一个不重复的整数数组 nums 。 最大二叉树 可以…

基于java+springboot+vue实现的医院门诊信息管理系统(文末源码+Lw+ppt)23-325

摘 要 系统根据现有的管理模块进行开发和扩展&#xff0c;采用面向对象的开发的思想和结构化的开发方法对医院门诊信息的现状进行系统调查。采用结构化的分析设计&#xff0c;该方法要求结合一定的图表&#xff0c;在模块化的基础上进行系统的开发工作。在设计中采用“自下而…

不敢想象吧!Anzo Capital发现不仅经济事件影响汇率天气也是

在投资交易中弄懂汇率的走势方向&#xff0c;对各位投资者的交易盈利那还不是小菜一碟&#xff0c;但各位投资者你们想象不到吧&#xff01;Anzo Capital发现不仅经济事件能影响汇率&#xff0c;就连天气也能轻易影响汇率。 就用2015年1月15日的经济事件来说&#xff0c;当瑞…

pandas读写excel,csv

1.读excel 1.to_dict() 函数基本语法 DataFrame.to_dict (self, orientdict , into ) --- 官方文档 函数种只需要填写一个参数&#xff1a;orient 即可 &#xff0c;但对于写入orient的不同&#xff0c;字典的构造方式也不同&#xff0c;官网一共给出了6种&#xff0c…

基于springboot+vue的流浪动物管理系统

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、阿里云专家博主、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战&#xff0c;欢迎高校老师\讲师\同行交流合作 ​主要内容&#xff1a;毕业设计(Javaweb项目|小程序|Pyt…