Redis解决缓存击穿问题——两种方法

目录

引言

解决办法

        互斥锁(强一致,性能差)

        逻辑过期(高可用,性能优)

设计逻辑过期时间


引言

        缓存击穿:给某一个key设置了过期时间,当key过期的时候,恰好这个时间点对这个key有大量的并发请求过来,这些并发的请求可能会瞬间把DB压垮

解决办法

        互斥锁(强一致,性能差)

                

 根据图片就可以看出,我们的思路就是只能让一个线程能够进行访问Redis,要想实现这个功能,我们也可以使用Redis自带的setnx

封装两个方法,一个写key来尝试获取锁另一个删key来释放锁

/*** 尝试获取锁** @param key* @return*/
private boolean tryLock(String key) {Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);
}/*** 释放锁** @param key*/
private void unlock(String key) {stringRedisTemplate.delete(key);
}

在并行情况下每当其他线程想要获取锁,来访问缓存都要通过将自己的key写到tryLock()方法里,setIfAbsent()返回false则说明有线程在在更新缓存数据,锁未释放。若返回true则说明当前线程拿到锁了可以访问缓存甚至操作缓存。
我们在下面一个热门的查询场景中用代码用代码来实现互斥锁解决缓存击穿,代码如下:

    /*** 解决缓存击穿的互斥锁* @param id* @return*/public Shop queryWithMutex(Long id) {String key = CACHE_SHOP_KEY + id;//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);  //JSON格式//2.判断是否存在if (StrUtil.isNotBlank(shopJson)) { //不为空就返回 此工具类API会判断" "为false//存在则直接返回Shop shop = JSONUtil.toBean(shopJson, Shop.class);//return Result.ok(shop);return shop;}//3.判断是否为空值 这里过滤 " "的情况,不用担心会一直触发这个条件因为他有TTLif (shopJson != null) {//返回一个空值return null;}//4.缓存重建 Redis中值为null的情况//4.1获得互斥锁String lockKey = "lock:shop"+id;Shop shopById=null;try {boolean isLock = tryLock(lockKey);//4.2判断是否获取成功if (!isLock){//4.3失败,则休眠并重试Thread.sleep(50);return queryWithMutex(id);}//4.4成功,根据id查询数据库shopById = getById(id);//5.不存在则返回错误if (shopById == null) {//将空值写入RedisstringRedisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);//为什么这里要存一个" "这是因为如果后续DB中有数据补充的话还可以去重建缓存//return Result.fail("暂无该商铺信息");return null;}//6.存在,写入RedisstringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shopById), CACHE_SHOP_TTL, TimeUnit.MINUTES);} catch (InterruptedException e) {throw new RuntimeException(e);} finally {//7.释放互斥锁unlock(lockKey);}return shopById;}

        逻辑过期(高可用,性能优)

        方案:用户查询某个热门产品信息,如果缓存未命中(即信息为空),则直接返回空,不去查询数据库。如果缓存信息命中,则判断是否逻辑过期,未过期返回缓存信息,过期则重建缓存,尝试获得互斥锁,获取失败则直接返回已过期缓存数据,获取成功则开启独立线程去重构缓存然后直接返回旧的缓存信息,重构完成之后就释放互斥锁。

封装一个方法用来模拟更新逻辑过期时间与缓存的数据在测试类里运行起来达到数据与热的效果 

/*** 添加逻辑过期时间** @param id* @param expireTime*/
public void saveShopRedis(Long id, Long expireTime) {//查询店铺信息Shop shop = getById(id);//封装逻辑过期时间RedisData redisData = new RedisData();redisData.setData(shop);redisData.setExpireTime(LocalDateTime.now().plusSeconds(expireTime));//将封装过期时间和商铺数据的对象写入RedisstringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, JSONUtil.toJsonStr(redisData));
}

查询接口:

/*** 逻辑过期解决缓存击穿** @param id* @return*/
public Shop queryWithLogicalExpire(Long id) throws InterruptedException {String key = CACHE_SHOP_KEY + id;Thread.sleep(200);//1.从Redis查询缓存String shopJson = stringRedisTemplate.opsForValue().get(key);  //JSON格式//2.判断是否存在if (StrUtil.isBlank(shopJson)) {//不存在则直接返回return null;}//3.判断是否为空值if (shopJson != null) {//返回一个空值//return Result.fail("店铺不存在!");return null;}//4.命中//4.1将JSON反序列化为对象RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);Shop shop = JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class);LocalDateTime expireTime = redisData.getExpireTime();//4.2判断是否过期if (expireTime.isAfter(LocalDateTime.now())) {//5.未过期则返回店铺信息return shop;}//6.过期则缓存重建//6.1获取互斥锁String LockKey = LOCK_SHOP_KEY + id;boolean isLock = tryLock(LockKey);//6.2判断是否成功获得锁if (isLock) {//6.3成功,开启独立线程,实现缓存重建CACHE_REBUILD_EXECUTOR.submit(() -> {try {//重建缓存this.saveShop2Redis(id, 20L);} catch (Exception e) {throw new RuntimeException(e);} finally {//释放锁unlock(LockKey);}});}//6.4返回商铺信息return shop;
}

设计逻辑过期时间

可以用这个方法设置逻辑过期时间

  import org.redisson.Redisson;
import org.redisson.api.RBucket;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;public class RedissonExample {public static void main(String[] args) {Config config = new Config();config.useSingleServer().setAddress("redis://127.0.0.1:6379");RedissonClient redisson = Redisson.create(config);String key = "exampleKey";String value = "exampleValue";int timeout = 10; // 过期时间(秒)// 获取RBucket对象RBucket<String> bucket = redisson.getBucket(key);// 设置值并指定过期时间bucket.set(value, timeout, TimeUnit.SECONDS);System.out.println("设置成功");redisson.shutdown();}
}

 大家可以看到,逻辑过期锁就是可以实现并发,所以他的效率更快,性能更好

但是

  • 牺牲了数据的实时性,以保证高并发场景下的服务可用性和数据库的稳定性。

  • 在实际应用中,需要确保获取互斥锁的操作是原子的,并且锁具有合适的超时时间,以避免死锁的发生。

  • 逻辑过期策略适用于那些对数据实时性要求不高,但要求服务高可用性的场景。

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

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

相关文章

Object 转 JSONObject 并排除null和““字符串

public static JSONObject objToJSONObject(Object obj) throws Exception{//创建一个 HashMap 对象 map&#xff0c;用于存储对象的属性名和属性值。//key 是属性名&#xff08;String 类型&#xff09;&#xff0c;value 是属性值&#xff08;Object 类型&#xff09;Map<…

python实现接口自动化

代码实现自动化相关理论 代码编写脚本和工具实现脚本区别是啥? 代码&#xff1a; 优点&#xff1a;代码灵活方便缺点&#xff1a;学习成本高 工具&#xff1a; 优点&#xff1a;易上手缺点&#xff1a;灵活度低&#xff0c;有局限性。 总结&#xff1a; 功能脚本&#xff1a;工…

C++特性——RAII、智能指针

RAII 就像new一个需要delete&#xff0c;fopen之后需要fclose&#xff0c;但这样会有隐形问题&#xff08;忘记释放&#xff09;。RAII即用对象把这个过程给包起来&#xff0c;对象构造的时候&#xff0c;new或者fopen&#xff0c;析构的时候delete. 为什么需要智能指针 对于…

算法系列——有监督学习——4.支持向量机

一、概述 支持向量机&#xff08;Support Vector Machine&#xff0c;SVM&#xff09;是一种应用范围非常广泛的算法&#xff0c;既可以用于分类&#xff0c;也可以用于回归。 本文将介绍如何将线性支持向量机应用于二元分类问题&#xff0c;以间隔&#xff08;margin&#x…

网络安全之前端学习(HTML篇)

前言&#xff1a;网络安全中有一个漏洞叫xss漏洞&#xff0c;就是利用网页引发弹窗&#xff0c;这就要求我们看得懂源码&#xff0c;所以我会持续更新前端学习&#xff0c;可以不精通&#xff0c;但是一定要会&#xff0c;主要掌握HTML&#xff0c;css&#xff0c;js这三项技术…

大华HTTP协议在智联视频超融合平台中的接入方法

一. 大华HTTP协议介绍 大华HTTP协议是大华股份&#xff08;Dahua Technology&#xff09;为其安防监控设备开发的一套基于HTTP/HTTPS的通信协议&#xff0c;主要用于设备与客户端&#xff08;如PC、手机、服务器&#xff09;之间的数据交互。该协议支持设备管理、视频流获取、…

介绍一下TiDB、RocksDb、levelDB、LSM 树、SSTable。

LSM 树&#xff08;Log-Structured Merge-Tree&#xff09; 核心原理&#xff1a;通过将随机写转换为顺序写优化写入性能&#xff0c;适用于写密集型场景。数据首先写入内存中的 MemTable&#xff08;有序结构&#xff0c;如跳表&#xff09;&#xff0c;当达到阈值后转为 Imm…

Powershell WSL导出导入ubuntu22.04.5子系统

导出Linux子系统 导出位置在C盘下,根据自己的实际情况更改即可Write-Host "export ubuntu22.04.5" -ForegroundColor Green wsl --export Ubuntu-22.04 c:\Ubuntu-22.04.tar 导入Linux子系统 好处是目录可用在任意磁盘路径,便于迁移不同的设备之间Write-Host &quo…

Jmeter

Jmeter 官网地址 https://jmeter.apache.org/ 下载安装包 系统环境变量 变量值JMETER_HOMEG:\Application\apache-jmeter-5.6.3 修改成自己的安装目录CLASSPATH;%JMETER_HOME%\lib\ext\ApacheJMeter_core.jar; %JMETER_HOME%\lib\jorphan.jar;Path%JMETER_HOME%\bin 中文界…

碰一碰发视频saas系统技术源头一站式开发文档

碰一碰发视频系统技术源头一站式开发文档 一、引言 在数字化信息传播高速发展的当下&#xff0c;如何让视频分享更便捷、高效&#xff0c;成为商家和开发者们关注的焦点。“碰一碰发视频”系统以其独特的交互方式和强大的功能优势&#xff0c;为视频分享领域带来了革命性变革。…

Execution failed for task ‘:path_provider_android:compileDebugJavaWithJavac‘.

What went wrong: Execution failed for task ‘:path_provider_android:compileDebugJavaWithJavac’. Could not resolve all files for configuration ‘:path_provider_android:androidJdkImage’. Failed to transform core-for-system-modules.jar to match attributes {…

学习笔记 ASP.NET Core Web API 8.0部署到iis

一.修改配置文件 修改Program.cs配置文件将 if (app.Environment.IsDevelopment()) {app.UseSwagger();app.UseSwaggerUI(); }修改为 app.UseSwagger(); app.UseSwaggerUI(); 二.安装ASP.NET Core Runtime 8.0.14 文件位置https://dotnet.microsoft.com/en-us/download/do…

一、小雅自带tutorial文件教程

下载压缩包 这是elegantrl的开源地址: ElegantRL 我使用的是云服务器平台&#xff0c;上传压缩包之后&#xff0c;使用Linux解压缩命令&#xff0c;将压缩包解压。 使用conda新建一个虚拟环境 conda create -n ElegantRL conda activate ElegantRL安装elegantrl包 pip ins…

海康ISAPI协议在智联视频超融合平台中的接入方法

一. 海康ISAPI协议详解 海康ISAPI协议原理 海康ISAPI&#xff08;Internet Server Application Programming Interface&#xff09;协议是海康威视设备提供的一种基于HTTP/HTTPS的高级通信协议&#xff0c;用于实现客户端与设备之间的数据交互。其核心原理包括&#xff1a; 基…

【Azure 架构师学习笔记】- Azure Databricks (22) --Autoloader

本文属于【Azure 架构师学习笔记】系列。 本文属于【Azure Databricks】系列。 接上文 【Azure 架构师学习笔记】- Azure Databricks (21) --费用相关 前言 Databricks家里在Apache Spark之上&#xff0c;是企业级的应对大规模数据处理的通用平台&#xff0c; 可以运行在AWS&a…

论文略读(2025.3.18-更新中)

关于可控视频生成 I2V3D: Controllable image-to-video generation with 3D guidance Image to Video工作&#xff0c;能够实现给一张图&#xff0c;输出一个视频&#xff0c;且可以控制相机。动态信息来自于用户手工设计&#xff08;相机移动&#xff0c;人体骨骼驱动&#x…

Excel导出工具类--复杂的excel功能导出(使用自定义注解导出)

Excel导出工具类 前言: 简单的excel导出,可以用easy-excel, fast-excel, auto-poi,在导出实体类上加上对应的注解,用封装好的工具类直接导出,但对于复杂的场景, 封装的工具类解决不了,要用原生的excel导出(easy-excel, fast-excel, auto-poi都支持原生的) 业务场景: 根据…

神策数据接入 DeepSeek,AI 赋能数据分析与智能运营

在 AI 技术迅猛发展的浪潮下&#xff0c;神策数据正在加速推进人工智能在数据分析和智能运营领域的深度应用。近日&#xff0c;神策数据宣布全面接入 DeepSeek&#xff0c;为企业客户带来更加智能化、高效的数据分析与智能运营服务。这一举措展现了神策数据在人工智能方向的探索…

头歌 JAVA 桥接模式实验

目录 任务描述 实现方式 编程要求 测试说明 代码实现 总结 1 任务描述 某软件公司欲开发一个数据转换工具&#xff0c;可以将数据库中的数据转换成多种文件格式&#xff0c;例如 TXT、XML、PDF 等格式&#xff0c;同时该工具需要支持多种不同的数据库。 本关任务&#…

安全无事故连续天数计算,python 时间工具的高效利用

安全天数计算&#xff0c;数据系统时间直取&#xff0c;安全标准高效便捷好用。 笔记模板由python脚本于2025-03-17 23:50:52创建&#xff0c;本篇笔记适合对python时间工具有研究欲的coder翻阅。 【学习的细节是欢悦的历程】 博客的核心价值&#xff1a;在于输出思考与经验&am…