Redis-分布式锁实现方式

news2024/11/16 1:44:55

在这里插入图片描述

文章目录

    • Redis分布式锁的作用?
    • Redis分布式锁的底层原理实现?
    • Redis分布式锁的应用场景?
    • Redis分布式锁遇到相关的场景问题?
      • 死锁问题
      • 锁超时问题
      • 归一问题
      • 可重入问题
      • 阻塞与非阻塞问题
      • 公平锁(Fair Lock)
      • 公平锁(Fair Lock)
    • 本篇小结

更多相关内容可查看

Redis分布式锁的作用?

Redis分布式锁是一种通过Redis实现的锁机制,用于在分布式系统中控制对共享资源的访问,以确保在同一时间只有一个客户端或进程可以对资源进行操作。以下是Redis分布式锁的主要作用:

  • 互斥访问控制: Redis分布式锁可确保在分布式环境下对共享资源的互斥访问。当多个客户端同时尝试获取锁时,只有一个客户端能成功获取,其他客户端将被阻塞或进行重试。
  • 资源保护: 分布式锁可用于保护共享资源的一致性和完整性。当多个客户端需要对某一资源执行操作时,通过获取分布式锁,确保每个操作按顺序执行,避免数据损坏或冲突。
  • 并发控制: Redis分布式锁允许对某个资源进行并发访问的控制。通过限制同一时间内只有一个客户端可以获取锁,确保对资源的并发访问不会导致竞态条件或不一致的结果。
  • 避免重复任务: 分布式锁可用于防止重复执行特定任务,特别是在定时任务或异步处理场景下。通过获取锁后执行任务,其他客户端在获取锁失败时知道任务已经在执行,避免重复执行。
  • 防止任务重入: Redis分布式锁可防止同一客户端对同一资源进行重入操作。当一个客户端已经持有锁时,其他请求同一个资源的操作将被阻塞或进行重试,确保资源只被一个客户端处理。

Redis分布式锁的底层原理实现?

Redis分布式锁主要依靠一个SETNX指令实现的 , 这条命令的含义就是“SET if Not Exists”,即不存在的时候才会设置值。只有在key不存在的情况下,将键key的值设置为value。如果key已经存在,则SETNX命令不做任何操作。

这个命令的返回值如下。

● 命令在设置成功时返回1。
● 命令在设置失败时返回0。

例:假设此时有线程A和线程B同时访问临界区代码,假设线程A首先执行了SETNX命令,并返回结果1,继续向下执行。而此时线程B再次执行SETNX命令时,返回的结果为0,则线程B不能继续向下执行。只有当线程A执行DELETE命令将设置的锁状态删除时,线程B才会成功执行SETNX命令设置加锁状态后继续向下执行

Boolean isLocked = stringRedisTemplate.opsForValue().setIfAbsent(PRODUCT_ID, "binghe");

Redis分布式锁的应用场景?

可应用于面试题

  • 参考回答一 : 在我最近做的一个项目中 , 我们在任务调度的时候使用了分布式锁 早期我们在进行定时任务的时候我们采用的是SpringTask实现的 , 在集群部署的情况下, 多个节点的定时任务会同时执行 ,造成重复调度
    , 影响运算结果, 浪费系统资源 这里为了防止这种情况的发送, 我们使用Redis实现分布式锁对任务进行调度管理
    ,防止重复任务执行,后期因为我们系统中的任务越来越多 , 执行规则也比较多 , 而且单节点执行效率有一定的限制 ,
    所以定时任务就切换成了XXL-JOB ,系统中就没有再使用分布式锁了
  • 参考回答二 : 我们项目在下单的过程中为了防止订单超卖 , 使用了分布式锁
  • 参考回答三 : 我们项目中有一个用户预约充电桩的功能, 为了避免多个用户预约到同一个充电桩, 使用可分布式锁
  • 参考回答四 : 我们项目中有一个抢座功能 , 为了避免同一个座位被多个用户购买, 使用了分布式锁

Redis分布式锁遇到相关的场景问题?

死锁问题

在使用分布式锁的时候, 如果因为一些原因导致系统宕机, 锁资源没有被释放, 就会产生死锁

解决的方案 : 上锁的时候设置锁的超时时间

Boolean isLocked = stringRedisTemplate.opsForValue().setIfAbsent(PRODUCT_ID, "binghe", 30, TimeUnit.SECONDS);

锁超时问题

如果业务执行需要的时间, 超过的锁的超时时间 , 这个时候业务还没有执行完成, 锁就已经自动被删除了,其他请求就能获取锁, 操作这个资源 , 这个时候就会出现并发问题 ,

解决的方案 :

  • 入Redis的watch dog机制, 自动为锁续期
  • 开启子线程 , 每隔20S运行一次, 重新设置锁的超时时间

归一问题

  • 如果一个线程获取了分布式锁, 但是这个线程业务没有执行完成之前 , 锁被其他的线程删掉了 , 又会出现线程并发问题 ,
  • 这个时候就需要考虑归一化问题 就是一个线程执行了加锁操作后,后续必须由这个线程执行解锁操作,加锁和解锁操作由同一个线程来完成。

解决的方案 : 为了解决只有加锁的线程才能进行相应的解锁操作的问题,那么,我们就需要将加锁和解锁操作绑定到同一个线程中,可以使用ThreadLocal来解决这个问题, 加锁的时候生成唯一标识保存到ThreadLocal , 并且设置到锁的值中 , 释放锁的时候, 判断线程中的唯一标识和锁的唯一标识是否相同, 只有相同才会释放

public class RedisLockImpl implements RedisLock{
 @Autowired
 private StringRedisTemplate stringRedisTemplate;

 private ThreadLocal<String> threadLocal = new ThreadLocal<String>();

 @Override
 public boolean tryLock(String key, long timeout, TimeUnit unit){
     String uuid = UUID.randomUUID().toString();
     threadLocal.set(uuid);
     return stringRedisTemplate.opsForValue().setIfAbsent(key, uuid, timeout, unit);
 }
 @Override
 public void releaseLock(String key){
     //当前线程中绑定的uuid与Redis中的uuid相同时,再执行删除锁的操作
     if(threadLocal.get().equals(stringRedisTemplate.opsForValue().get(key))){
       stringRedisTemplate.delete(key);   
     }
 }
}

可重入问题

当一个线程成功设置了锁标志位后,其他的线程再设置锁标志位时,就会返回失败。
还有一种场景就是在一个业务中, 有个操作都需要获取到锁, 这个时候第二个操作就无法获取锁了 , 操作会失败

场景示例 :

下单业务中, 扣减商品库存会给商品加锁, 增加商品销量也需要给商品加锁 , 这个时候需要获取二次锁 第二次获取商品锁就会失败 ,这就需要我们的分布式锁能够实现可重入

解决方案 : 实现可重入锁最简单的方式就是使用计数器 , 加锁成功之后计数器 + 1 , 取消锁之后计数器 -1 , 计数器减为0 , 真正从Redis删除锁

public class RedisLockImpl implements RedisLock{
 @Autowired
 private StringRedisTemplate stringRedisTemplate;

 private ThreadLocal<String> threadLocal = new ThreadLocal<String>();

 private ThreadLocal<Integer> threadLocalInteger = new ThreadLocal<Integer>();

 @Override
 public boolean tryLock(String key, long timeout, TimeUnit unit){
     Boolean isLocked = false;
     if(threadLocal.get() == null){
         String uuid = UUID.randomUUID().toString();
      threadLocal.set(uuid);
         isLocked = stringRedisTemplate.opsForValue().setIfAbsent(key, uuid, timeout, unit);
     }else{
         isLocked = true;   
     }
     //加锁成功后将计数器加1
     if(isLocked){
         Integer count = threadLocalInteger.get() == null ? 0 : threadLocalInteger.get();
         threadLocalInteger.set(count++);
     }
     return isLocked;
 }

 @Override
 public void releaseLock(String key){
     //当前线程中绑定的uuid与Redis中的uuid相同时,再执行删除锁的操作
     if(threadLocal.get().equals(stringRedisTemplate.opsForValue().get(key))){
         Integer count = threadLocalInteger.get();
         //计数器减为0时释放锁
         if(count == null || --count <= 0){
           stringRedisTemplate.delete(key);      
         }
     }
 }
}

阻塞与非阻塞问题

在使用分布式锁的时候 , 如果当前需要操作的资源已经加了锁, 这个时候会获取锁失败, 直接向用户返回失败信息 , 用户的体验非常不好 , 所以我们在实现分布式锁的时候, 我们可以将后续的请求进行阻塞,直到当前请求释放锁后,再唤醒阻塞的请求获得分布式锁来执行方法。

解决的方案 : 参考自旋锁的思想, 获取锁失败自选获取锁, 直到成功为止 , 当然为了防止多条线程自旋带来的系统资料消耗, 可以设置一个自旋的超时时间 , 超过时间之后, 自动终止线程 , 返回失败信息

@0verride
public boolean tryLock(String key, long timeout, TimeUnit unit)
{
	Boolean isLocked = false;
	if(threadLocal.get()== null){
	String uuid = UUID.randomUUID().tostring();
	threadLocal.set(uuid);
	isLocked =stringRedisTemplate.opsForValue().setIfAbsent(key, uuid,timeout,unit):/如果获取锁失败则自旋获取锁,自到成工
	if(!isLocked)
	{
		for(;;){
			isLocked = stringRedisTemplate.opsForValue().setIfAbsent(key, uuid, timeout, unit)
			if(isLocked){
				break;
			}
		}
	}else{
		isLocked = true;
		}
	//加锁成功后将计数器加1
		if(isLocked){
		Integer count = threadLocalInteger.get()== null ?0threadLocalInteger.get();
		threadLocalInteger.set(count++);
		}
				
		return isLocked:
}

公平锁(Fair Lock)

  • 公平锁保证锁的获取按照请求的顺序进行,即先请求锁的线程先获取锁,遵循先来先服务的原则。
  • 公平锁的实现会维护一个请求队列,当锁被释放时,队列中的第一个请求线程会被唤醒并获得锁。

公平锁(Fair Lock)

  • 非公平锁在锁释放时不会考虑请求锁的顺序,它允许某个后来的请求线程在当前锁被释放时立即尝试获取锁。
  • 非公平锁的优势在于它可以减少锁竞争的开销,尤其是在高并发情况下,因为它允许某些线程在等待队列中绕过排队直接获取锁。

本篇小结

其他Redis的相关问题链接如下
Redis数据持久化策略
Redis数据过期策略
Redis数据淘汰策略
Redis集群方案
Redis主从同步
Redis分片集群如何存储及读取数据
Redis跟Mysql如何保证数据一致性
Redis的缓存穿透、缓存击穿、缓存雪崩及解决方案

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

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

相关文章

【每日力扣】207. 课程表 与 208. 实现 Trie (前缀树)

&#x1f525; 个人主页: 黑洞晓威 &#x1f600;你不必等到非常厉害&#xff0c;才敢开始&#xff0c;你需要开始&#xff0c;才会变的非常厉害 207. 课程表 你这个学期必须选修 numCourses 门课程&#xff0c;记为 0 到 numCourses - 1 。 在选修某些课程之前需要一些先修课…

实现 Flask 应用的 HTTPS 加密保护

文章目录 1. 获得免费的 SSL 证书步骤&#xff1a; 2. 配置 Flask 应用3. 测试和部署结论结论 在今天的网络环境中&#xff0c;保护网站和用户数据的安全至关重要。通过在 Flask 应用中启用 HTTPS 加密&#xff0c;您可以确保用户的数据在传输过程中得到保护。本文将介绍如何结…

vue3修改eldialog弹窗css不生效

问题&#xff1a;子组件中的eldialog没有父标签 直接使用如下是不生效的 .el-dialog{ top: 10%; } 解决&#xff1a; 加一个父标签 使用deep深度查询 .dialogClass :deep(.el-dialog) { top: 10%; } 就可以修改了

# 从浅入深 学习 SpringCloud 微服务架构(十八)

从浅入深 学习 SpringCloud 微服务架构&#xff08;十八&#xff09; 一、开源配置中心 Apollo&#xff1a;概述 1、开源配置中心 Apollo Apollo -A reliable configuration management system Apollo(阿波罗)是携程框架部门研发的分布式配置中心&#xff0c;能够集中化管理…

研究变压器感应耐压试验电源的能效和节能潜力

变压器感应耐压试验电源 变压器感应耐压试验电源在电源行业中是一种非常重要的设备&#xff0c;用于测试和验证变压器的绝缘性能。变压器感应耐压试验电源是一种专门为变压器感应耐压试验而设计的电源设备。它的工作原理基于变压器的感应原理&#xff0c;利用感应电压来对变压器…

【科学研究】创造力的阴暗面——恶意创造力

::: block-1 “时问桫椤”是一个致力于为本科生到研究生教育阶段提供帮助的不太正式的公众号。我们旨在在大家感到困惑、痛苦或面临困难时伸出援手。通过总结广大研究生的经验&#xff0c;帮助大家尽早适应研究生生活&#xff0c;尽快了解科研的本质。祝一切顺利&#xff01;—…

CheckStyle静态样式之道

优质博文&#xff1a;IT-BLOG-CN 在标准化的统一样式检查规范里&#xff0c;最为常用的统一样式工具是checkstyle插件&#xff0c;而不是国内阿里的代码规约插件。 【1】下载插件 【2】配置生效 配置生效及告警设置 【3】配置checkstyle.xml 官网地址 官网最新Releases 下面…

SV-6007 网络对讲求助终端 隧道对讲求助广播终端

SV-6007 网络对讲求助终端 隧道对讲求助广播终端 一、描述 SV-6007 网络对讲求助终端 隧道对讲求助广播终端 SV-6007是我司的一款壁挂式双按键求助对讲终端&#xff0c;具有10/100M以太网接口&#xff0c;其接收网络的音频数据&#xff0c;实时解码播放&#xff0c;还配置了…

照片尺寸怎么修改?这几个图片处理方式都可以

修改图片尺寸在许多场景中都是常见的需求&#xff0c;包括网页设计、图片编辑、手机应用程序开发、幻灯片演示、社交媒体和博客、以及打印和出版物设计&#xff0c;通过调整图片大小&#xff0c;可以适应不同的布局和设备要求&#xff0c;那么问题来了&#xff0c;如何将图片改…

Vue3实战笔记(21)—自定义404页面

文章目录 前言一、标题1二、通过守卫导航配置404总结 前言 一个精致的404页面对于网站的用户体验至关重要。404页面&#xff0c;也称为“未找到”页面&#xff0c;是在用户尝试访问网站中不存在或已删除的页面时显示的。 一、标题1 404都很熟悉了&#xff0c;vue3默认找不到界…

C#学习笔记12:Winform网页操作-CefSharp内嵌浏览器

今日学习使用Winform操作网页&#xff0c;先从从窗体内嵌一个浏览器开始吧&#xff1a; 文章提供测试代码讲解、测试效果图、整体测试工程下载 目录 CefSharp介绍与安装&#xff1a; 创建解决方案安装CefSharp&#xff1a; 控件放置&#xff1a; 整体代码贴出&#xff1a; 更改…

数据结构与算法学习笔记十---链队列的表示和实现(C++)

目录 前言 1.队列的概念 2.队列的表示和实现 1.定义 2.初始化 ​编辑 3.销毁队列 4.清空队列 5.队列判空 6.队列长度 7.获取队头元素 8.入队 9.出队 10.遍历 11.完整代码 前言 这篇博客主要讲的是对队列的链式存储。 1.队列的概念 队列是一种访问受限的线性表。…

知识图谱开发日志

应用于应用环境的配置.测试.发布 假如你写了一个web,并且测试调试都没有问题 并且,你想发给你的朋友,导师,或者部署到远程云服务器上 那么,你需要配置相同的软件,比如数据库,web服务器,必要的插件,库,etc…但这并不一定能保证软件的正常运行,因为别人可能使用完全不同的操作系统…

Facebook广告运营黑五类怎么投?

哈喽呀&#xff0c;很多小伙伴不知道黑五具体是哪些今天就跟大家来说说&#xff0c;黑五类是指一些擦边的受到限制的产品&#xff0c;指的是药品、医疗器械、丰胸、减肥、增高这五类产品。 黑五类产品可以在哪些平台进行投放&#xff1a; 目前黑五类可以广告投放的跨境电商平台…

第三方组件element-ui

1、创建 选vue2 不要快照 vue2于vue3差异 vue2main。js import Vue from vue import App from ./App.vueVue.config.productionTip falsenew Vue({render: h > h(App), }).$mount(#app)vue3 main.js vue2不能有多个跟组件&#xff08;div&#xff09; 代码&#xff1a;Mo…

ssm教材管理系统

ssm教材管理系统 一、主要技术点 ssm,easypoi(对excel导入导出)&#xff0c;下拉列表二级联动&#xff0c;live-2d看板娘&#xff0c;echartjs图表&#xff0c;图片上传下载。。 二、主要业务逻辑 管理员可以增删改查教材、教材商、入库教材、用户&#xff08;用户包括学生…

2024年京东618红包领取口令是什么?2024年618京东红包活动时间是从什么时候开始到几号结束?

2024年京东618红包活动时间 京东618红包活动时间是从2024年5月28日开始&#xff0c;一直持续到6月18日结束。 2024年京东618红包领取方式 在2024年京东618活动时间内&#xff0c;每天都可以打开手机京东APP&#xff0c;输入框搜索红包领取口令「 天降红包882 」&#xff0c;搜…

Java——对象的打印

当我们运行如下代码&#xff1a; public class Person {String name;String gender;int age;public Person(String name,String gender,int age){this.name name;this.gender gender;this.age age;}public static void main(String[] args){Person person new Person(&quo…

Google I/O 2024 干货全解读:Gemini AI 横空出世,智能未来触手可及!

Google I/O 2024 干货全解读&#xff1a;Gemini AI 横空出世&#xff0c;智能未来触手可及&#xff01; 博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》…

从新手到高手,教你如何改造你的广告思维方式!

想要广告震撼人心又让人长时间记住&#xff1f;答案肯定是“创意”二字。广告创意&#xff0c;说白了就是脑洞大开&#xff0c;想法新颖。那些很流行的广告&#xff0c;都是因为背后的想法特别、新颖。做广告啊&#xff0c;就得不停地思考&#xff0c;创新思维是关键。 广告思…