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();}
}
大家可以看到,逻辑过期锁就是可以实现并发,所以他的效率更快,性能更好
但是
-
牺牲了数据的实时性,以保证高并发场景下的服务可用性和数据库的稳定性。
-
在实际应用中,需要确保获取互斥锁的操作是原子的,并且锁具有合适的超时时间,以避免死锁的发生。
-
逻辑过期策略适用于那些对数据实时性要求不高,但要求服务高可用性的场景。
相关文章:
Redis解决缓存击穿问题——两种方法
目录 引言 解决办法 互斥锁(强一致,性能差) 逻辑过期(高可用,性能优) 设计逻辑过期时间 引言 缓存击穿:给某一个key设置了过期时间,当key过期的时候,恰好这个时间点对…...
前端 Blob 详解
前端 Blob 详解 1. 什么是 Blob? Blob(Binary Large Object)表示二进制大对象,用于存储二进制数据。在前端开发中,Blob 常用于处理文件、图像、视频等二进制数据。 2. 创建 Blob 可以通过 Blob 构造函数创建 Blob …...
Debezium + Kafka-connect 实现Postgres实时同步Hologres
基于 Debezium Kafka 的方案实现 PostgreSQL 到 Hologres 的实时数据同步,是一种高可靠性、高扩展性的解决方案。以下是详细的实现步骤: 1. 方案架构 Debezium:捕获 PostgreSQL 的变更数据(CDC),并将变更…...
JavaScript性能优化的12种方式
当涉及到JavaScript性能优化时,有几个关键的方面需要考虑。下面是一些常见的JavaScript性能优化技巧和实践: 减少DOM操作: 频繁的DOM操作会导致重绘和重新布局,影响性能。建议将多个DOM操作合并为一个操作,或者使用Do…...
在Ubuntu上安装MEAN Stack的4个步骤
在Ubuntu上安装MEAN Stack的4个步骤为:1.安装MEAN;2.安装MongoDB;3.安装NodeJS,Git和NPM;4.安装剩余的依赖项。 什么是MEAN Stack? 平均堆栈一直在很大程度上升高为基于稳健的基于JavaScript的开发堆栈。…...
集成学习之随机森林
目录 一、集成学习的含义 二、集成学习的代表 三、集成学习的应用 1、分类问题集成。(基学习器是分类模型) 2、回归问题集成。(基学习器是回归模型) 3、特征选取集成。 四、Bagging之随机森林 1、随机森林是有多个决策树&a…...
在线JSON格式校验工具站
在线JSON校验格式化工具(Be JSON)在线,JSON,JSON 校验,格式化,xml转json 工具,在线工具,json视图,可视化,程序,服务器,域名注册,正则表达式,测试,在线json格式化工具,json 格式化,json格式化工具,json字符串格式化,json 在线查看器,json在线,json 在线验…...
SAP的WPS导出找不到路径怎么办;上载报错怎么办
一.打开注册编辑器 二.输入以下地址 计算机\HKEY_CLASSES_ROOT\ExcelWorksheet\Protocol\StdFileEditing\Server 去除掉EXE后面的命令即可 二:WPS上载文件没反应怎么办 如何切换整合模式或多组件模式-WPS学堂 根据官方操作把整合模式改成多组件模式...
Moonlight-16B-A3B: 变革性的高效大语言模型,凭借Muon优化器打破训练效率极限
近日,由Moonshot AI团队推出的Moonlight-16B-A3B模型,再次在AI领域引发了广泛关注。这款全新的Mixture-of-Experts (MoE)架构的大型语言模型,凭借其创新的训练优化技术,特别是Muon优化器的使用,成功突破了训练效率的极…...
rust学习笔记17-异常处理
今天聊聊rust中异常错误处理 1. 基础类型:Result 和 Option,之前判断空指针就用到过 Option<T> 用途:表示值可能存在(Some(T))或不存在(None),适用于无需错误信息的场景。 f…...
PyTorch系列教程:使用预训练语言模型增强文本分类
文本分类仍是自然语言处理(NLP)领域的一项基础任务,其目标是将文本数据归入预先设定的类别之中。预训练语言模型的出现极大地提升了这一领域的性能。本文将探讨如何利用 PyTorch 来利用这些模型,展示它们如何能增强文本分类任务。…...
LabVIEW 线性拟合
该 LabVIEW 程序实现了 线性拟合(Linear Fit),用于计算给定一组数据点的斜率(Slope)和截距(Intercept),并将结果可视化于 XY Graph 中。本案例适用于数据拟合、实验数据分析、传感器…...
nacos安装,服务注册,服务发现,远程调用3个方法
安装 点版本下载页面 服务注册 每个微服务都配置nacos的地址,都要知道 服务发现 2个是知道了解 远程调用基本实现 远程调用方法2,负载均衡API测试 远程调用方法3,注解 负载均衡的远程调用, 总结 面试题...
k8s主要控制器简述(一)ReplicaSet与Deployment
目录 一、ReplicaSet 关键特性 示例 解释 支持的 Operator 二、Deployment 1. 声明式更新 示例 2. 滚动更新 示例 3. 回滚 示例 4. ReplicaSet 管理 示例 5. 自动恢复 示例 6. 扩展和缩容 示例 示例 一、ReplicaSet ReplicaSet 是 Kubernetes 中的一个核心控…...
Java中的消息中间件对比与解析:RocketMQ vs RabbitMQ
消息中间件(Message Queue, MQ)是分布式系统中实现异步通信、解耦服务和流量削峰的关键组件。在Java生态中,RocketMQ和RabbitMQ是两个广泛应用的消息队列系统,但它们在设计理念、功能特性和适用场景上存在显著差异。本文将从核心功…...
Android14 Log.isLoggable判断的分析
Android14 Log.isLoggable判断的分析 文章目录 Android14 Log.isLoggable判断的分析一、前言二、答案和分析1、Log.isLoggable 设置成true2、Log.isLoggable 分析(1)Log.java(2)android_util_Log.cpp(3)pro…...
Mac:JMeter 下载+安装+环境配置(图文详细讲解)
📌 下载JMeter 下载地址:https://jmeter.apache.org/download_jmeter.cgi 📌 无需安装 Apache官网下载 JMeter 压缩包,无需安装,下载解压后放到自己指定目录下即可。 按我自己的习惯,我会在用户 jane 目…...
Python IP解析器 ip2region使用
说明:最近需要在python项目内使用IP定位所在城市的需求,没有采用向外部ISP服务商API请求获取信息的方案,则翻了翻,在搞Java时很多的方案,在Python端反而可选择范围很小。 # 示例查询 ips ["106.38.188.214"…...
labview与西门子1500plc进行S7通讯(仿真效果)
环境: 1.博图V16 2.S7-PLCSIM Advanced V3.0 3.labview2020 4.HslCommunication的dll文件 运行效果图 通过使用HslCommunication的库文件来对西门子plc进行通讯 labview代码 代码打包 通过网盘分享的文件:labview进行s7通讯测试.rar 链接: https:/…...
Oracle 公布 Java 的五大新功能
Java 增强提案包括语言增强和性能优化,从 JDK 25 中的稳定值 API 开始。 随着JDK(Java 开发工具包)24刚刚全面上市,Oracle 提前透露了不久的将来即将推出的 Java 功能,包括增强原始装箱到空限制值类类型。 3 月 18 日…...
台式机电脑组装---电脑机箱与主板接线
台式机电脑组装—电脑机箱与主板接线 1、机箱连接主板的跳线一般主要有USB 2.0、USB 3.0、前置音频接口(HD_AUDIO)以及POWER SW、RESET SW、POWER LED、HDD LED四个主板跳线,这些跳线分别的含义如下。 RESET SW:机箱重启按键;注:…...
ubuntu高并发内核参数调优 - (压测客户端调优)
业务上要求集群提供10w并发,10w并发听上去不是很难,但10w并发持续1小时呢 在业务上线之前还需要我们自己对业务进行压测,俗称benchmark。 压测的服务器也是需要进行性能调优的,以下列出调优前后的参数对比,更直观的分析…...
动作捕捉手套如何让虚拟现实人机交互 “触手可及”?
在虚拟与现实逐渐交融的当下,动作捕捉技术正以前所未有的速度革新着多个领域。 动作捕捉技术,简称“动捕”,已经从早期的影视特效制作,逐步拓展到游戏开发、虚拟现实、机器人控制等多个领域。 而mHandPrO数据手套作为这一领域的…...
笔记本电脑关不了机是怎么回事 这有解决方法
在快节奏的现代生活中,笔记本电脑已成为我们工作、学习和娱乐的得力助手。在使用电脑的过程中,笔记本电脑突然关不了机了,怎么回事?下面驱动人生就来讲一讲笔记本电脑不能正常关机的解决方法,有需要的可以来看看。 一、…...
数据库:一文掌握 MongoDB 的各种指令(MongoDB指令备忘)
文章目录 入门连接 MongoDB Shell显示数据库切换数据库显示集合运行 JavaScript 文件 CRUD创建寻找文件使用运算符查找文档读取更新删除 数据库和集合Drop创建集合其他采集功能 索引列表索引创建索引删除索引隐藏/取消隐藏索引 方便的命令 其它改变流分片集群副本集 MongoDB 此…...
麒麟操作系统作为服务器,并且需要在浏览器上调试 MATLAB
在内网环境下,使用麒麟操作系统作为服务器,并且需要在浏览器上调试 MATLAB 程序,这确实复杂,但仍然有可行的解决方案。麒麟操作系统是国产化的 Linux 发行版(如基于 Ubuntu Kylin 或银河麒麟),因…...
给管理商场消防安全搭建消防安全培训小程序全过程
一、需求沟通 “我是管理商场消防安全的嘛,做这个的作用呢,1是商场的所有商户员工可以看平面或者视频随时自学, 2是我们定期培训必修课程、考试,这个需要留存他们的手签字的签到表确认我们讲给他们听了(免责很重要&am…...
Flutter:页面滚动,导航栏背景颜色过渡动画
记录:导航默认透明,页面发生滚动后,导航背景色由0-1,过渡到白色背景。 view import package:ducafe_ui_core/ducafe_ui_core.dart; import package:flutter/material.dart; import package:get/get.dart; import package:redo…...
VSCode + CMake
参考文献: 如何用 GCC, CMake 和 Make 编译C/C代码Windows 上的 Linux 子系统:WSLWSL:桌面 UI 远程连接 RDP 配置 VScode 文章目录 CMake 配置VSCode 配置launch.jsontask.jsonc_cpp_properties.json CMake 配置 编写如下的 CmakeLists.t…...
Docker进阶篇1:什么是Docker数据卷?为什么需要Docker数据卷?Docker数据卷3种类型介绍
大家好我是木木,在当今快速发展的云计算与云原生时代,容器化技术蓬勃兴起,Docker 作为实现容器化的主流工具之一,为开发者和运维人员带来了极大的便捷 。下面我们一起开始进阶第1篇:什么是Docker数据卷?为什…...
