SpringBoot整合Redis:缓存击穿--互斥锁解决

🎉🎉欢迎光临,终于等到你啦🎉🎉

🏅我是苏泽,一位对技术充满热情的探索者和分享者。🚀🚀

🌟持续更新的专栏Redis实战与进阶

本专栏讲解Redis从原理到实践

这是苏泽的个人主页可以看到我其他的内容哦👇👇

努力的苏泽icon-default.png?t=N7T8http://suzee.blog.csdn.net


最近超级无敌忙  就断更好久了  实在是抽不出时间来 没办法  这篇文章也只是整理我以前学习的资料  目前还有一整套企业级的Redis处理方案没写哈 敬请期待朋友们

下面是正文


目录

首先我们要明白什么是缓存击穿

分析有什么办法能解决

业务解析

​编辑

代码实现:

然后我们将缓存穿透的函数给封装起来

原函数

我们将这部分逻辑 封装到 queryWithPassThrough中

我们再写一个函数queryWithMutex来用互斥锁解决缓存穿透的问题

于是原本的查询函数的结构就变成了这样

这样做的优缺点

优点:

缺点:

下一篇我们来讲解如何使用另一个方案解决这个问题


首先我们要明白什么是缓存击穿

Redis缓存击穿是指在高并发的情况下,当某个热点数据的缓存过期或不存在时,大量的请求同时涌入数据库或后端服务,导致数据库或后端服务负载过高,甚至崩溃的情况。

分析有什么办法能解决

当涉及到并发访问共享资源时,互斥锁和逻辑过期是两种常用的技术手段。

  1. 互斥锁(Mutex):
    互斥锁是一种并发控制机制,用于在多个线程或进程之间保证共享资源的互斥访问。它通过在关键代码段前后设置锁来确保同一时间只有一个线程或进程可以执行关键代码段。当某个线程或进程获取到互斥锁时,其他线程或进程需要等待锁的释放才能继续执行。

业务解析

代码实现:

先写两个函数 一个加锁 一个释放锁

private boolean tryLock(String key){//自定义互斥锁  将申请锁的结果返回Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10L, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}
//释放锁
private void unLock(String key){stringRedisTemplate.delete(key);}

然后我们将缓存穿透的函数给封装起来

原函数

public Result queryById(Long id) {//1.从Redis查询id  这里使用的数据结构可以是String也可以是hash  若是查询不到就为空了 CACHE_SHOP_KEY就是"cache:shop:"String shopJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);//2.判断是否存在 在字符串意义上是否为空if (StrUtil.isNotBlank(shopJson)) {//3.存在直接返回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return  Result.ok(shop);}//3.判断是否命中写入redis中的nullif(shopJson != null){return Result.fail("店铺信息不能为空!");}//4.不存在 查询数据库Shop shop = getById(id);//5.数据库中不存在 返回报错if (shop == null){//空值写入redisstringRedisTemplate.opsForValue().set("cache:shop:" + id, null,CACHE_NULL_TTL, TimeUnit.MINUTES);return Result.fail("店铺不存在!");}//6.数据库中存在  写入Redis  并返回stringRedisTemplate.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(shop),30L, TimeUnit.MINUTES);return Result.ok(shop);
}

我们将这部分逻辑 封装到 queryWithPassThrough中

//封装缓存穿透函数
private Shop queryWithPassThrough(Long id){//1.从Redis查询id  这里使用的数据结构可以是String也可以是hash  若是查询不到就为空了 CACHE_SHOP_KEY就是"cache:shop:"String shopJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);//2.判断是否 在字符串意义上是否为空if (StrUtil.isNotBlank(shopJson)) {//3.存在直接返回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return  shop;}//3.判断是否命中写入redis中的nullif(shopJson != null){return null;}Shop shop = getById(id);//5.数据库中不存在 返回报错if (shop == null){//空值null写入redisstringRedisTemplate.opsForValue().set("cache:shop:" + id, null,CACHE_NULL_TTL, TimeUnit.MINUTES);return null;}//6.数据库中存在  写入Redis  并返回stringRedisTemplate.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(shop),30L, TimeUnit.MINUTES);return shop;
}

我们再写一个函数queryWithMutex来用互斥锁解决缓存穿透的问题

于是原本的查询函数的结构就变成了这样

public Result queryById(Long id) {//缓存穿透
//        Shop shop = queryWithPassThrough(id);//互斥锁解决缓存穿透Shop shop = queryWithMutex(id);if (shop == null) {return Result.fail("店铺不存在");}//返回return Result.ok(shop);}
//解决缓存穿透的问题
private Shop queryWithMutex(Long id) throws InterruptedException{String lockKey = "lockKey" + id;while (true) {//1.从Redis查询id  这里使用的数据结构可以是String也可以是hash  若是查询不到就为空了 CACHE_SHOP_KEY就是"cache:shop:"String shopJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);//2.判断是否 在字符串意义上是否为空if (StrUtil.isNotBlank(shopJson)) {//3.存在直接返回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return  shop;}//3.判断是否命中写入redis中的nullif(shopJson != null){return null;}//4重建缓存//4.1申请互斥锁boolean flag = tryLock(lockKey);//4.2判断是否成功if (!flag){try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}else//成功了就跳出循环  这里不敢用递归 怕栈溢出了break;}//4.4 成功 查询数据库Shop shop = getById(id);//5.数据库中不存在 返回报错if (shop == null){//空值null写入redisstringRedisTemplate.opsForValue().set("cache:shop:" + id, null,CACHE_NULL_TTL, TimeUnit.MINUTES);return null;}//6.数据库中存在  写入Redis  并返回stringRedisTemplate.opsForValue().set("cache:shop:" + id, JSONUtil.toJsonStr(shop),30L, TimeUnit.MINUTES);//7.释放互斥锁unLock(lockKey);return shop;
}

这样做的优缺点

互斥锁作为一种并发控制机制,在解决缓存击穿问题时具有以下优点和缺点:

优点:

  1. 确保数据一致性:互斥锁可以确保同一时间只有一个线程或进程可以访问共享资源,避免了并发访问导致的数据不一致性问题。

  2. 避免竞态条件:互斥锁可以防止多个线程或进程同时执行关键代码段,避免了竞态条件的发生。竞态条件是指多个线程或进程对共享资源的访问顺序不确定,导致结果的不可预测性。

  3. 简单易用:互斥锁的使用相对简单,可以通过加锁和解锁操作来控制对共享资源的访问。

缺点:

  1. 性能开销:互斥锁在多线程环境下会引入一定的性能开销。当多个线程竞争同一个锁时,其他线程需要等待锁的释放,这会导致一些线程的阻塞和等待,降低系统的并发性能。

  2. 可能引发死锁:如果在使用互斥锁时处理不当,可能会发生死锁的情况。死锁是指多个线程或进程相互等待对方持有的资源,导致所有线程都无法继续执行。

  3. 容易导致线程饥饿:当某个线程持有互斥锁并长时间不释放时,其他线程可能会一直等待锁的释放,导致线程饥饿现象出现。

下一篇我们来讲解如何使用另一个方案解决这个问题

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

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

相关文章

flutter Got socket error trying to find package nested at

flutter Got socket error trying to find package nested at xxx 报错信息:“Got socket error trying to find package nested at” 通常出现在Flutter尝试从pub.dev获取依赖包时,由于网络问题导致无法连接到pub.dev或者无法正确解析包的路径。 例如&…

遥感卫星影像质量评价指标汇总

1. 主观评价方法 以人为图像的评价者,根据自己的评价尺度和经验对图像质量进行评价。 2. 客观评价方法 1)均方差 2)信噪比 主要用来评价影像经压缩、传输、增强等处理前后的质量变化情况,其本质与均方差类似。 3)方差 反映了图像各个像元灰度相对…

【前端面试3+1】03深拷贝浅拷贝、let和var、css盒模型、【有效括号】

一、深拷贝浅拷贝 深拷贝和浅拷贝都是用于复制对象或数组的概念,但它们之间有着重要的区别: 1. 浅拷贝: 浅拷贝是指在拷贝对象或数组时,只会复制一层对象的属性或元素,而不会递归地复制嵌套的对象或数组。因此&#xf…

Kubeflow文档1:介绍与架构

Kubeflow 2024/3/19版本的文档 此专栏用来展示相关的内容翻译,重点关注本地部署,关于运营商的方案,请自行查阅 文档地址https://www.kubeflow.org/docs/ 开始编辑时间:2024/3/27;最后编辑时间2024/3/27 Kubeflow文…

记录关于智能家居的路程的一个bug___Segmentation fault(段错误)

前言 其实发生段错误的情况有很多: 其实在项目的开发中最有可能的错误就是①和②,考虑到本项目数组用的比较少,所以主要是考虑错误①指针的误用。 有时候错误就是那么离谱,声音也算是一种设备??&#xff…

论文笔记:Retrieval-Augmented Generation forAI-Generated Content: A Survey

北大202402的RAG综述 1 intro 1.1 AICG 近年来,人们对人工智能生成内容(AIGC)的兴趣激增。各种内容生成工具已经精心设计,用于生产各种模态下的多样化对象 文本&代码:大型语言模型(LLM)…

抖音短视频矩阵系统源代码开发部署/SaaS贴牌/源码开源

抖音短视频矩阵系统的源代码开发部署可以分为以下几个步骤: 确定需求:首先,你需要确定你想要开发的具体功能和特性,比如用户注册、上传短视频、评论等。 开发源代码:根据需求,你可以选择使用合适的编程语言…

深入理解element-plus table二次封装:从理论到实践的全面指南

前言 在许多中后台管理系统中,表格占据着半壁江山,如果使用element plus组件库,那么少不了要用到table组件,可是table组件的功能过于基础,因此,我在table组件的实现基础之上进一步封装,从而实现…

装饰器模式实战运用(功能增强)

目录 前言 装饰器模式与代理模式的区别 UML plantuml 类图 实战代码 mybatis cache 前言 装饰器模式和代理模式在使用上很相似,都是在不修改原始类代码的情况下,动态地给真实对象的方法做增强。 装饰器模式是通过创建一个包装对象来包裹原有对象…

Vue时间组件:Dayjs与Moment对比

🤍 前端开发工程师、技术日更博主、已过CET6 🍨 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 🕠 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 🍚 蓝桥云课签约作者、上架课程《Vue.js 和 E…

python opencv稍基础初学

傅里叶变换 傅里叶变换f​​​​​傅里叶分析之掐死教程(完整版)更新于2014.06.06 - 知乎 (zhihu.com)https://zhuanlan.zhihu.com/p/19763358 相当nice 傅里叶变换的作用 高频:变化剧烈的灰度分量,例如边界 低频:变…

Centos 配置JDK和Tomcat(新手版)

Centos 配置JDK和Tomcat(新手版) 1、安装JDK 如果原环境有jdk则需要卸载。 先用命令查看 rpm -qa|grep java 如果有jdk则需要卸载rpm -e --nodeps java-1.7.0-openjdk-1.7.0.191-2.6.15.5.el7.x86_64rpm -e --nodeps java-1.8.0-openjdk-…