【SpringBoot篇】解决缓存击穿问题① — 基于互斥锁方式
文章目录
- 🌹什么是缓存击穿
- 🌺基于互斥锁解决问题
- 🛸思路
- 🏳️🌈代码实现

🌹什么是缓存击穿
缓存击穿是指在使用缓存系统时,对一个热点数据的高并发请求导致缓存失效,多个请求同时访问数据库,造成数据库压力过大,性能下降。
具体来说,缓存击穿通常发生在以下情况下:
- 热点数据失效:当某个热点数据的缓存过期或被删除时,此时如果有大量的并发请求同时访问该数据,缓存系统无法命中缓存,每个请求都会直接访问数据库。
- 频繁更新数据:某个数据被频繁地修改,导致缓存频繁失效,而此时大量的请求同时访问该数据,造成缓存击穿。
缓存击穿会严重影响系统的性能和可用性,因为数据库无法处理如此高的并发请求,导致系统响应变慢甚至崩溃。
但是对于缓存击穿,我们有什么方法可以解决呢
🌺基于互斥锁解决问题
互斥锁(Mutex)是一种并发编程中用于保护共享资源的机制,它可以确保在同一时刻只有一个线程可以访问共享资源,从而避免多个线程同时对共享资源进行读写操作而导致的数据竞争和不确定性行为。
互斥锁的主要特点包括:
- 独占性:当一个线程获得了互斥锁后,其他线程就无法再获得该互斥锁,直到持有该锁的线程释放它。
- 阻塞和等待:如果一个线程尝试获取已被其他线程持有的互斥锁,那么它会被阻塞,直到该互斥锁被释放。
- 原子性:互斥锁的获取和释放操作是原子的,不会被打断。
互斥锁通常用于以下场景:
- 在多线程环境下保护共享资源,如共享变量、共享数据结构等,防止多个线程同时修改造成数据不一致。
- 控制对临界区的访问,确保同一时间只有一个线程能够执行临界区代码,以避免竞态条件(Race Condition)的发生。
🛸思路
使用互斥锁来解决缓存击穿问题的思路是通过对关键代码块进行加锁
,保证在同一时间只有一个线程能够执行这段代码。这样可以有效地避免多个线程同时访问数据库,减轻数据库的压力,提高系统的性能和可用性。
在解决缓存击穿问题时,通常会使用互斥锁锁住以下几个关键步骤:
- 检查缓存:首先检查缓存中是否存在所需数据。
- 缓存失效处理:如果缓存中不存在所需数据,即缓存失效,需要进行进一步处理。
- 加锁:在进行缓存失效处理之前,获取互斥锁,确保只有一个线程能够执行后续的数据库查询和缓存更新操作。
- 数据查询和缓存更新:在成功获得互斥锁之后,执行数据库查询操作,获取所需数据,并将数据更新到缓存中。
- 释放锁:缓存更新完成后,释放互斥锁,允许其他等待的线程获得锁并从缓存中获取数据。
通过加锁的方式,保证了同一时间只有一个线程能够执行关键代码块,避免了缓存击穿问题。其他线程在等待期间可以从缓存中获取旧数据,而不会直接访问数据库。这样可以减少数据库的并发访问压力,提升了系统的并发能力和性能。
需要注意的是,互斥锁的使用应该谨慎,避免持有锁的时间过长,否则可能会导致其他线程的延迟和性能下降。在设计时,要权衡锁的粒度和性能需求,确保互斥锁的使用场景合理,并根据具体情况选择合适的锁机制(如读写锁、分布式锁等)进行优化。
🏳️🌈代码实现
我们看下面的例子
@Service
public class ShopServiceImpl extends ServiceImpl<ShopMapper, Shop> implements IShopService {@Resourceprivate StringRedisTemplate stringRedisTemplate;@Overridepublic Result queryById(Long id) {//缓存穿透
// Shop shop=queryWithPassThrough(id);//互斥锁解决缓存击穿Shop shop=queryWithMutex(id);if(shop==null){return Result.fail("店铺不存在");}//返回return Result.ok(shop);}public Shop queryWithMutex(Long id){String key=CACHE_SHOP_KEY+":"+id;//从redis中查询缓存String shopJson=stringRedisTemplate.opsForValue().get(key);//判断是否存在if(StrUtil.isNotBlank(shopJson)){//存在,直接返回return JSONUtil.toBean(shopJson, Shop.class);}//判断命中的是否是空值if(shopJson!=null){//返回一个错误信息return null;}//实现缓存重建//获取互斥锁String lockKey="lock:shop"+id;Shop shop=null;try {boolean isLock=tryLock(lockKey);//判断是否获取成功if (!isLock){//失败,那么休眠并且重试Thread.sleep(100);return queryWithMutex(id);}//成功,则根据id查询数据库shop=getById(id);//不存在,返回错误if(shop==null){//将空值写入到redisstringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL,TimeUnit.MINUTES);return null;}//存在,写入到redis里面stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL,TimeUnit.MINUTES);}catch (Exception e){throw new RuntimeException(e);}finally {//释放互斥锁unlock(lockKey);}//返回return shop;}//存在,写入到redis里面stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop),CACHE_SHOP_TTL,TimeUnit.MINUTES);//返回return shop;}//获取锁private boolean tryLock(String key){Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}//释放锁private void unlock(String key){stringRedisTemplate.delete(key);}
}
在技术的道路上,我们不断探索、不断前行,不断面对挑战、不断突破自我。科技的发展改变着世界,而我们作为技术人员,也在这个过程中书写着自己的篇章。让我们携手并进,共同努力,开创美好的未来!愿我们在科技的征途上不断奋进,创造出更加美好、更加智能的明天!
相关文章:

【SpringBoot篇】解决缓存击穿问题① — 基于互斥锁方式
文章目录 🌹什么是缓存击穿🌺基于互斥锁解决问题🛸思路 🏳️🌈代码实现 🌹什么是缓存击穿 缓存击穿是指在使用缓存系统时,对一个热点数据的高并发请求导致缓存失效,多个请求同时访…...

系列一、GitHub搜索技巧
一、GitHub搜索技巧 1.1、概述 作为程序员,GitHub大家应该都再熟悉不过了,很多时候当我们需要使用某一项技能而又无从下手时,通常会在百度(面向百度编程)或者在GitHub上通过关键字寻找相关案例,比如我想学…...
35.java后端面试宝典
一、自我介绍。 我叫什么,这次是应聘什么岗位,会什么技术,会什么框架,熟练掌握框架之间的整合技术,做过什么项目并且介绍主要做了什么,并且可以为公司带来什么价值。 总的来说,初级Java工程师岗…...

Linux 磁盘空间占满故障解决方法
故障排查: 使用命令查看磁盘使用量 # 使用人类可读的格式(预设值是不加这个选项的...) df -h # --inodes 列出 inode 资讯,不列出已使用 block df -i # 查看当前目录下各个文件及目录占用空间大小 du -sh / 情况一:一般磁盘空间满了&a…...

让生活更智能,P1600边缘智能网关带你进入智能家居新时代
一、什么是P1600边缘智能网关? 在科技日新月异的今天,我们的生活已经被各种智能产品所包围。而在这个智能化的浪潮中,P1600边缘智能网关以其独特的优势,成为了智能家居的重要组成部分。那么,什么是P1600边缘智能网关呢…...
Java与前端:2023年的真实状况与焦虑解读
一、引言 在2023年,IT圈中流传着一些关于Java和前端的言论,这些言论引起了广泛的关注。有些人认为“Java已死、前端已凉”,而另一些人则持不同观点。那么,这些言论背后的真相是什么?它们是在贩卖焦虑吗?本…...
adb 基本命令合集
1.获取所有的包信息: adb shell pm list packages com.yu.weskul 2.清除APP缓存 adb shell pm clear <package-name> 3.查看当前应用及Activity adb shell dumpsys window | findstr "mCurrentFocus" 4.查看应用详细信息 adb shell dumpsys pack…...
[RK-Linux] RK3399支持M.2 NVMe SSD启动
延续《[RK-Linux] 从主线U-Boot移植PCIe及其PHY驱动到RK3399 U-Boot》 启动流程: maskrom -> loader(从 eMMC 存储器加载) -> u-boot(从 eMMC 存储器加载)-> kernel (从 M.2 NVMe SSD 加载)-> rootfs (从 M.2 NVMe SSD 挂载)配置从 M.2 NVMe SSD 启动: …...

LTO-3 磁带机种草终于是用上了
跑来跑去,买了不少配件,终于是把这磁带机给用上了,已经备份好了300 多 GB 的数据。 我们用了 NAS 的数据压缩功能,把需要备份的文件用 NAS 压缩成一个 Zip 文件,如果你可以 tar 的话也行。 这样传输速度更快…...
【全网首发】洛谷P1020 [NOIP1999 提高组] 导弹拦截
P1020 导弹拦截 の 题目传送门。 解题思路 显然,第一问求的是最长不上升子序列。 于是接下来直接抛开第一问不谈,也不考虑优化,直接考虑第二问。待会就知道原因了。 引理:Dilworth 定理 狄尔沃斯定理亦称偏序集分解定理&#…...

trino-435版本windows下源码编译
一、源码下载地址 https://github.com/trinodb/trino/tags 二、编译环境及工具准备 1、maven (1)版本:3.6.3 (2)settings.xml配置 <?xml version"1.0" encoding"UTF-8"?> <settin…...

java类和对象的思想概述
0.面向对象Object OOP——名人名言:类是写出来的,对象是new出来的 **> 学习面向对象的三条路线 java类以及类成员:(重点)类成员——属性、方法、构造器、(熟悉)代码块、内部类面向对象特征&…...
ant design vue3中引入message消息提示,全局引入亲测有效
两种方式 第一种:使用provide和inject方式 第二种:使用全局挂载$message方式 第一种: //main.ts import { createApp } from vue; import App from ./App; import Antd,{ message } from ant-design-vue; import ant-design-vue/es/mess…...

UE5 Landscape 制作GIS卫星图地形
1. 总体想法: 制作GIS地形,使用Landscaping MapBox是一个好方法,但是区域过大,会占用很多内存 https://blog.csdn.net/qq_17523181/article/details/135029614 如果采用QGis,导出卫星图,在UE5里拼合出地形…...

opencv入门到精通——改变颜色空间
目录 目标 改变颜色空间 对象追踪 如何找到要追踪的HSV值? 目标 在本教程中,你将学习如何将图像从一个色彩空间转换到另一个,像BGR↔灰色,BGR↔HSV等 除此之外,我们还将创建一个应用程序,以提取视频中的…...

法线贴图实现地形模型皱褶、凹凸不平的纹理效果
在线工具推荐: 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 法线贴图在3D建模中扮演着重要的角色,它通过模拟表面的微…...

【SpringBoot篇】基于Redis实现生成全局唯一ID的方法
文章目录 🍔生成全局唯一ID🌹为什么要生成全局唯一id🌺生成全局id的方法✨代码实现 🍔生成全局唯一ID 是一种在分布式系统下用来生成全局唯一id的工具 在项目中生成全局唯一ID有很多好处,其中包括: 数据…...

轻度听力损失的儿童需要早期干预吗?
一些宝宝在做听力筛查时总是不通过,进一步听力诊断发现宝宝有轻度的听力损失,刚知道这个消息时,家长可担心了,总想着宝宝是不是听不到啊?但是一段时间后,有些家长又会忽略宝宝的听力问题,因为部…...

【Spring Security】认证密码加密Token令牌CSRF的使用详解
🎉🎉欢迎来到我的CSDN主页!🎉🎉 🏅我是Java方文山,一个在CSDN分享笔记的博主。📚📚 🌟推荐给大家我的专栏《Spring Security》。🎯🎯 …...
python一点通: 一文讲清Post 和 Put操作区别!
当我们使用网络服务时,如果我们不能小心地区分 POST 和 PUT,有时可能会触发错误。 在 Web 开发世界中,特别是在处理 RESTful API 时,HTTP 方法 POST 和 PUT 经常被使用,但常常被误解。这两者都用于向服务器发送数据&a…...
【Linux】shell脚本忽略错误继续执行
在 shell 脚本中,可以使用 set -e 命令来设置脚本在遇到错误时退出执行。如果你希望脚本忽略错误并继续执行,可以在脚本开头添加 set e 命令来取消该设置。 举例1 #!/bin/bash# 取消 set -e 的设置 set e# 执行命令,并忽略错误 rm somefile…...
ssc377d修改flash分区大小
1、flash的分区默认分配16M、 / # df -h Filesystem Size Used Available Use% Mounted on /dev/root 1.9M 1.9M 0 100% / /dev/mtdblock4 3.0M...
渲染学进阶内容——模型
最近在写模组的时候发现渲染器里面离不开模型的定义,在渲染的第二篇文章中简单的讲解了一下关于模型部分的内容,其实不管是方块还是方块实体,都离不开模型的内容 🧱 一、CubeListBuilder 功能解析 CubeListBuilder 是 Minecraft Java 版模型系统的核心构建器,用于动态创…...

Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...
相机Camera日志分析之三十一:高通Camx HAL十种流程基础分析关键字汇总(后续持续更新中)
【关注我,后续持续新增专题博文,谢谢!!!】 上一篇我们讲了:有对最普通的场景进行各个日志注释讲解,但相机场景太多,日志差异也巨大。后面将展示各种场景下的日志。 通过notepad++打开场景下的日志,通过下列分类关键字搜索,即可清晰的分析不同场景的相机运行流程差异…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
Java线上CPU飙高问题排查全指南
一、引言 在Java应用的线上运行环境中,CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时,通常会导致应用响应缓慢,甚至服务不可用,严重影响用户体验和业务运行。因此,掌握一套科学有效的CPU飙高问题排查方法&…...

群晖NAS如何在虚拟机创建飞牛NAS
套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...
tomcat指定使用的jdk版本
说明 有时候需要对tomcat配置指定的jdk版本号,此时,我们可以通过以下方式进行配置 设置方式 找到tomcat的bin目录中的setclasspath.bat。如果是linux系统则是setclasspath.sh set JAVA_HOMEC:\Program Files\Java\jdk8 set JRE_HOMEC:\Program Files…...
go 里面的指针
指针 在 Go 中,指针(pointer)是一个变量的内存地址,就像 C 语言那样: a : 10 p : &a // p 是一个指向 a 的指针 fmt.Println(*p) // 输出 10,通过指针解引用• &a 表示获取变量 a 的地址 p 表示…...