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

分布式锁的使用场景

  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.mzph.cn/news/760264.shtml

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

相关文章

算法51:动态规划专练(力扣139题,单词拆分)---从左往右尝试模型的误区

题目&#xff1a; 给你一个字符串 s 和一个字符串列表 wordDict 作为字典。如果可以利用字典中出现的一个或多个单词拼接出 s 则返回 true。 注意&#xff1a;不要求字典中出现的单词全部都使用&#xff0c;并且字典中的单词可以重复使用。 示例 1&#xff1a; 输入: s &qu…

如何实现手机遥控端关机按钮同时关闭TV端和手机端界面

目前家庭电视机主要通过其自带的遥控器进行操控&#xff0c;实现的功能较为单一。例如&#xff0c;当我们要在TV端搜索节目时&#xff0c;电视机在遥控器的操控下往往只能完成一些字母或数字的输入&#xff0c;而无法输入其他复杂的内容。分布式遥控器将手机的输入能力和电视遥…

基于springboot+vue的智慧生活商城系统

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

Pytest自动化测试执行环境切换的两种解决方案(超详细)

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 点击文末小卡片 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 痛点分析 在实际企业的项目中&#xff0c;自动化测试的代码往往需要在不同的环境中进行切换&am…

蓝桥:重新排序(差分,python)

前言&#xff1a; 本题在模拟考试时还不会差分法&#xff0c;用纯暴力思路ac了60%的案列&#xff0c;之后看了蓝桥讲解&#xff0c;用差分做出来了&#xff08;但对差分还是一知半解&#xff09;&#xff0c;最近学会了差分又来做本题&#xff0c;又卡在了技巧思路上&#xff…

【超细完整版】C# WebService 通过URL生成WSDL文件和DLL文件 【生成篇】

目的 支持通过web url (自适应“?wsdl”的有无) 生成.wsdl文件 和 .dll文件 实现 将通过一个类的三部分来实现这些功能 获取url中的ClassName &#xff08;GetClassNameFromUrl&#xff09;转换为WSDL文件 &#xff08;UrlToWsdlFile&#xff09;转换为DLL文件 &#xff08;…

【算法】数组-二分搜索法

对应力扣704题目 左闭右闭 public class Solution{ public static void main(String[] args){ int[] nums {1,2,3,4,5,6,7,8,9,10}; int target 5; Solution solution new Soulution(); int result solution.search( nums, target); System.out.println("找到值的位置…

利用代理IP突破地域限制:解锁全球网络访问攻略

利用代理IP突破地域限制&#xff0c;实现解锁全球网络访问&#xff0c;是一种常见的网络技术手段。以下是一份详细的攻略&#xff1a; 1. 理解代理IP&#xff1a; 代理IP就像一个中间人&#xff0c;你的请求先发送到代理服务器&#xff0c;然后由代理服务器去获取你想要访问的网…

【STL基础】vector、stack、queue、list、pair、map、unordered_map、set、unordered_set(详细讲解)

vector、list、pair、unordered_map、unordered_set、stack、queue 参考文章&#xff1a; &#xff08;1&#xff09;【apollo】泛型编程 与 STL &#xff08;2&#xff09;c stack用法 入门必看 超详细 &#xff08;3&#xff09;C中queue的用法&#xff08;超详细&#xff0c…

C语言关于void类型的指针作为函数形参在使用时需要注意的坑

目录 前言 一、void*指针使用时不同编译器下的处理结果 二、void*指针传递的指针变量进行位运算时&#xff0c;一定要注意强制转换的类型&#xff0c;和值的取值范围 总结 前言 众所周知&#xff0c;void* 指针作为函数形参时&#xff0c;表示可以接受任意类型的参数&#xf…

C语言基础知识点(十七)结构体中只用指针来存储字符串

// 如果需要用结构存储字符串&#xff0c;用字符数组作为成员会比较简单 // 如果需要使用指向char的指针来进行存储&#xff0c;那么需要请求malloc来 // 为字符串分配合适的存储空间#include <stdio.h> #include <string.h> //提供strcpy()\strlen()的原型 #i…

Linux修改Redis密码

方法一&#xff1a;修改配置文件 找到Redis配置文件redis.conf&#xff0c;通常位于/etc/redis/或/usr/local/etc/目录 sudo vim /etc/redis/redis.conf找到requirepass指令&#xff0c;如果已经有这个指令&#xff0c;你可以直接修改密码&#xff0c;如果没有&#xff0c;添…

Python实战:上下文管理器与with语句

一、引言 在Python编程中&#xff0c;上下文管理器与with语句是实现资源高效管理的重要工具。当程序使用资源&#xff08;如文件、网络连接等&#xff09;时&#xff0c;我们需要确保这些资源在使用完毕后能够被正确地释放&#xff0c;以避免资源泄漏和程序崩溃。上下文管理器…

基于FFmpeg混流及录制rtmp直播流

1、什么是混流&#xff1f; 混流就是把多路音视频流合成单流。准确的说&#xff0c;混流应该叫做混音&#xff08;音频流&#xff09;混画面&#xff08;视频流&#xff09; 混流的过程包括解码、混流、编码和推流四个部分。混流这个环节包括做抖动缓冲&#xff0c;目的是把多…

GDPU Java 天码行空4

文章目录 一、实验目的二、实验内容及要求三、实验内容及要求1. 设计AnimalTool工具类&#xff0c;实现eat()函数多态性&#x1f496; AnimalDemo.java&#x1f338; 运行结果 2. 是猫是狗&#x1f496; DuoTaiDemo5.java&#x1f338; 运行结果 3. 太会了&#x1f496; DuoTai…

国内IP地址切换排行榜软件大全

随着互联网的飞速发展&#xff0c;IP地址切换技术在日常工作和生活中扮演着越来越重要的角色。无论是为了网络安全、访问特定地区网站&#xff0c;还是进行市场调研、网络爬虫等&#xff0c;IP地址切换都成为了不可或缺的工具。虎观代理将为您介绍国内较受欢迎的IP地址切换软件…

B004-springcloud alibaba 服务容错 Sentinel

目录 高并发带来的问题服务雪崩效应常见容错方案常见的容错思路隔离超时限流熔断降级 常见的容错组件 Sentinel入门什么是Sentinel微服务项目集成Sentinel核心库安装Sentinel控制台实现一个接口的限流 Sentinel的概念和功能基本概念重要功能 Sentinel规则流控规则三种流控模式三…

mybatis拦截器打印sql日志

前言 利用mybatis拦截器打印输出sql 操作 编写拦截器 package com.it2.excel01.interceptor;import java.text.DateFormat; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Properties;import lombok.extern.slf4j.Slf4j; impo…

深度学习——线性代数相关知识

线性代数基础知识 一、线性代数基础知识1、标量2、向量3、矩阵4、张量5、点积6、向量—矩阵积7、矩阵—矩阵乘法 二、小结 一、线性代数基础知识 本节将介绍简要地回顾一下部分基本线性代数内容&#xff0c;线性代数中的基本数学对象、算术和运算&#xff0c;并用数学符号和相…

基于SpringBoot的高校办公室行政事务管理系统

采用技术 基于SpringBoot的高校办公室行政事务管理系统的设计与实现~ 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;SpringBootMyBatis 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 页面展示效果 功能清单 教师信息管理 办公室管理 办公物资管…