黑马点评项目02——商户查询缓存(缓存穿透、缓存雪崩、缓存击穿)以及细节

article/2025/8/21 10:27:07

在这里插入图片描述

1.添加redis缓存

在这里插入图片描述
StringRedisTemplate 使用的是这个哈,有人可能有疑问,存放的是字符串吗,商铺值应该是个对象才对啊,在细节中解析
代码:

@Override
public Result queryById(Long id) {//查询redis,若存在则转换成对象后返回String key = CACHE_SHOP_KEY + id;String shopJson = stringRedisTemplate.opsForValue().get(key);if (StringUtils.isNotBlank(shopJson)) {Shop shop = JSONUtil.toBean(shopJson, Shop.class);return Result.ok(shop);}//不存在则查询数据库,然后转成以json串存⼊redis后,返回Shop shop = shopMapper.selectById(id);if(shop==null){return Result.fail("店铺不存在");}stringRedisTemplate.opsForValue().set(key,JSONUtil.toJsonStr(shop));return Result.ok(shop);
}

2.API 细节解析 json串与对象相互转换

stringRedisTemplate.opsForValue().get(key)的返回值是一个String,如果查不到,返回null,为了以防万一,HuTool工具判断 StringUtils.isNotBlank(shopJson),可以确保是确实是一个商铺。在这里插入图片描述
命中缓存,转换为将String json串转换为对象, Shop shop = JSONUtil.toBean(shopJson, Shop.class); 注意这个API,字符串转化为Shop;
不命中缓存,查数据库返回商铺Shop shop = shopMapper.selectById(id),
此时注意了,不能直接把对象放进去,要放进去一个json,也注意这个API。
stringRedisTemplate.opsForValue() .set(key,JSONUtil.toJsonStr(shop))

3.Redis缓存和数据库一致性策略

Cache Aside(旁路缓存)策略(适合读多写少)
在这里插入图片描述
注意:写的时候先更新数据库,这样也可能发生不一致问题,只是几率相对较小,一个解决策略就是加上延迟双删
在这里插入图片描述
在这里插入图片描述
另外,Cache Aside 策略适合读多写少的场景,不适合写多的场景,因为当写入比较频繁时,缓存中的数据会被频繁地清理,这样会对缓存的命中率有一些影响。

 //先更新数据库,再删除缓存shopMapper.updateById(shop);stringRedisTemplate.delete(CACHE_SHOP_KEY+ id);

4. 缓存穿透

缓存穿透 :是指客户端请求的数据在缓存中和数据库中都不存在,这样缓存永远不会生效,这些请求都会打到数据库。
常见的解决方案有两种:

4.1 缓存空对象

在原来的逻辑中,我们如果发现这个数据在mysql中不存在,直接就返回404了,这样是会存在缓存穿透问题的。现在,我如果查询到数据库没有这个对象时,我就往Redis存放(id:‘’)空字符串,下次你再访问,给你空字符串,根本过不了isBank()
在这里插入图片描述
缺点:
可能存在短时间不一致问题;占用内存
注意:缓存空值要设置较短的过期时间(如 5~10 分钟)

4.2 布隆过滤

直接拦截了,只要数据库中没有,当然可能会存在误判,不过概率较小!!!
4.3 其他方案
在这里插入图片描述

5. 缓存雪崩

缓存雪崩:是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,带来巨大压力。就是缓存Redis废了
在这里插入图片描述

6. 缓存击穿(例如:优惠劵信息id)

缓存击穿是指:某个热点 key访问频率极高)突然失效,大并发请求在同一时间全部打到数据库,短时间内数据库可能被压垮。

在这里插入图片描述

6.1 互斥锁

在这里插入图片描述

    /*模拟加锁*/private boolean tryLock(String key){Boolean b = stringRedisTemplate.opsForValue().setIfAbsent(key, "", 30, TimeUnit.SECONDS);return BooleanUtil.isTrue(b);}private void unlock(String key){stringRedisTemplate.delete(key);}

stringRedisTemplate.opsForValue().setIfAbsent(key, "", 30, TimeUnit.SECONDS) 拿到了锁就返回true在这里插入图片描述
Boolean.TRUE.equals(success)或者 BooleanUtil.isTrue(success)来判断
互斥锁逻辑

 public Result queryById(Long id) {// 1. 从 Redis 查询缓存String shopJson = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);// 2. 如果缓存命中,直接返回  必须有实际东西才可以if(StrUtil.isNotBlank(shopJson)){log.info("shopJson缓存中有:{}",shopJson);Shop shop = JSONUtil.toBean(shopJson, Shop.class);return Result.ok(shop);}// 3. 如果缓存中是空字符串或者占位符(说明数据库中查过确实不存在),返回错误if (shopJson != null) {return Result.fail("店铺信息不存在(缓存空值)");}// 4. 缓存未命中,准备查询数据库前先尝试加锁,防止缓存击穿String lockKey = "shop:lock" + id;boolean lock = tryLock(lockKey); // 尝试加锁 true 该线程拿到了锁Shop shop;try {if (lock) {// 5. 获取锁成功,查询数据库shop = getById(id);// 6. 数据库中也不存在,返回错误(此处未缓存空值,依赖布隆拦截)if (shop == null) {// return 之前会进入finallyreturn Result.fail("店铺不存在");}// 7. 查询成功,写入缓存String jsonStr = JSONUtil.toJsonStr(shop);stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, jsonStr, 3L, TimeUnit.MINUTES);} else {// 8. 获取锁失败,稍等后递归重试(等待其他线程完成缓存填充)Thread.sleep(50);// return 之前会进入finallyreturn queryById(id);}} catch (InterruptedException e) {throw new RuntimeException(e);} finally{// 9. 只有拿到锁的线程才释放锁,避免误删其他线程的锁if (lock) {unlock(lockKey);}}// 10. 返回结果return Result.ok(shop);}

6.2 逻辑过期

既然是高并发访问那干脆就直接redis里面一直都不要删除了,再加个逻辑过期时间,过期的话就开个独立线程去更新数据写入redis,在没更新完之前访问到的都是redis里面的旧数据。
在这里插入图片描述
具体实现见:逻辑过期解决缓存击穿

我只是讲一下难以实现的技术点:
1、需要封装一个实体类+过期时间一起构成RedisData对象,有两种实现方式。
第一种:泛型

@Data
public class RedisData<T> {private LocalDateTime expireTime;private T data;
}

第二种:Object

@Data
@AllArgsConstructor
@NoArgsConstructor
public class RedisData {private LocalDateTime expireTime;private Object data;
}

第一种就是序列化麻烦一些,不过更规范,api记住

     // 1. 查询 Redis 缓存String json = stringRedisTemplate.opsForValue().get(key);if (StrUtil.isBlank(json)) {// 缓存未命中return null;}// 2. 反序列化为带逻辑过期的数据结构RedisData<?> redisData = JSONUtil.toBean(json, RedisData.class);/*  JSONObject dataJson = (JSONObject) redisData.getData(); // 先转 JSONObjectT data = dataJson.toBean(type);*/T data = JSONUtil.toBean((JSONObject) redisData.getData(), type);LocalDateTime expireTime = redisData.getExpireTime();

接下来就是判断是否过期了,如果没有过期,直接返回data;如果过期了,尝试获取锁,注意有个细节,如果获取锁后一定要再判断一下是否从缓存中得到是否为空,为空,说明被删掉了,返回之前找的旧data,再判断这时候是不是不过期了,这样就少一次IO,不过期,说明有其他线程刚刚更新过了。
在这里插入图片描述
如果确实是过期,交给其他线程重建,

// 缓存重建线程池(用于异步更新缓存)
private static final ExecutorService CACHE_REBUILD_EXECUTOR =Executors.newFixedThreadPool(10);
  // 6. 异步线程池重建缓存CACHE_REBUILD_EXECUTOR.submit(() -> {try {// 查询数据库T fresh = dbFallback.apply(id);// 模拟重建缓存Thread.sleep(200);// 重新写入缓存(逻辑过期)this.setWithLogicalExpire(key, fresh, time, unit);} catch (Exception e) {e.printStackTrace();} finally {// 释放锁unlock(lockKey);}});

http://www.hkcw.cn/article/PRFoSKusZt.shtml

相关文章

python学习打卡day39

DAY 39 图像数据与显存 知识点回顾 图像数据的格式&#xff1a;灰度和彩色数据模型的定义显存占用的4种地方 模型参数梯度参数优化器参数数据批量所占显存神经元输出中间状态 batchisize和训练的关系 作业&#xff1a;今日代码较少&#xff0c;理解内容即可 1.图像数据的格式&a…

代码随想录打卡|Day50 图论(拓扑排序精讲 、dijkstra(朴素版)精讲 )

图论part08 拓扑排序精讲 代码随想录讲解链接 题目链接 思路 在这个题目之中&#xff0c;个别文件的处理依赖于别的文件&#xff0c;因此&#xff0c;文件的处理顺序十分重要。我们用图来表示文件的处理顺序&#xff0c;文件s指向文件t&#xff0c;则说明如果要正确的处理文…

朱啸虎:曾三次错失宁德时代!

朱啸虎:曾三次错失宁德时代。近日,在腾讯视频《激流第二季》中,投资人朱啸虎谈及自己三次错失宁德时代,投资电池失败损失7000万美金的经历。他表示早期看了宁德时代三次,认为他们的技术不性感,不是最新一代,因而投了掌握美国最新一代技术的波士顿电池。结果最新的技术不…

女子观赏“蓝眼泪”失踪多方搜救 游客夜观奇景失联

5月26日傍晚,浙江台州温岭市松门镇海边的大坑沙村,23岁游客孙女士在徒步时失踪。警方和救援人员在山上和海里搜寻多日,但截至29日上午仍未找到她。孙女士生于2002年,平时与父亲一起在宁波生活。5月26日,她告诉父亲自己出去玩,当晚不回家,随后乘列车前往温岭市。当天下午…

PS linux 基础篇1-AXI_DMA

系列文章目录 文章目录 系列文章目录前言一、AXI DMA ip核二、BD工程三、PS linux工程1.使用开源的xilinx_axidma-master工程验证驱动2.按照其他的开源进行就行&#xff0c;没什么写的了 前言 PL与PS之间快速的接口&#xff0c;本文为LOOP回环测试 一、AXI DMA ip核 MM2S mem…

儿子打死父亲后母亲欲顶罪母子被判 家庭悲剧引发深思

49岁的宋甲长期酗酒,酒后经常殴打、辱骂妻儿。2024年7月5日,宋甲喝了一斤多白酒后回家辱骂儿子宋乙,妻子李某上前劝阻却被殴打。看到母亲被家暴,儿子打了父亲一拳,父子发生争吵打斗,最终儿子将父亲打死。案发后,母子共同清理现场,并焚烧了作案工具。李某为了保护儿子,…

前端-关于apk文件分片上传

为什么需要分片上传&#xff1f; 一次性处理的致命缺陷&#xff1a; 内存溢出&#xff1a;大文件完全加载到内存 界面冻结&#xff1a;读取过程阻塞主线程 上传失败&#xff1a;单次请求可能超时或被服务器拒绝 需求&#xff1a;一个弹出框&#xff0c;将apk文件上传&#x…

病理切片TLS比例作为免疫治疗响应和预后的预测因子

定义 三级淋巴结构 &#xff08;TLS&#xff0c;Tumor Lymphoid Structure&#xff09;&#xff1a;是指在非淋巴组织中的慢性炎症部位&#xff08;包括癌症&#xff09;形成的异位淋巴细胞聚集体。 目前&#xff0c;分割和量化 TLS 的金标准是基于对 T&#xff08;CD3: 用于…

Linux浅谈

Linux浅谈 一、什么是 Linux&#xff1f;先抛开 “内核”&#xff0c;看整体 可以把 Linux 系统 想象成一台 “组装电脑”&#xff1a; 最核心的零件是 “主板”—— 这就是 Linux 内核&#xff08;Kernel&#xff09;&#xff0c;负责管理电脑里的所有硬件&#xff08;比如 …

【模板-指南】

这里写自定义目录标题 欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题&#xff0c;有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants 创建一个自定义列表如何创建一个…

ICASSP2025丨融合语音停顿信息与语言模型的阿尔兹海默病检测

阿尔兹海默病&#xff08;Alzheimers Disease, AD&#xff09;是一种以认知能力下降和记忆丧失为特征的渐进性神经退行性疾病&#xff0c;及早发现对于其干预和治疗至关重要。近期&#xff0c;清华大学语音与音频技术实验室&#xff08;SATLab&#xff09;提出了一种将停顿信息…

吴艳妮获亚锦赛季军 妈妈:希望她恢复最佳状态 带伤参赛展现坚韧

因当地暴雨天气,原本计划于5月29日下午5时进行的亚洲田径锦标赛女子100米栏决赛延迟至当晚9时开赛。中国选手吴艳妮以13秒068的成绩获得季军。5月28日上午,吴艳妮以13秒07的成绩晋级决赛。赛后,她的母亲熊艳表示,比赛结果并不重要,只希望她尽快恢复,以最佳状态迎接未来的…

AMBA-AHB仲裁机制

前文 仲裁机制保证了任意时刻只有一个 master 可以接入总线。Arbiter 决定了哪个向其发出接入请求的 master 可以接入总线&#xff0c;这通过优先级算法实现。AHB规范并没有给出优先级算法&#xff0c;需要设计者根据具体的系统要求定义。一般情况下 arbiter 不会中断一…

长期口臭可能是你的身体在求救 三步教你自救

有些人表面光鲜亮丽一张嘴却让人“退避三舍”尤其在晨起、空腹时口臭问题更明显不仅尴尬还可能暗藏健康隐患科学应对口臭还你清新口气!先对号入座你的口臭是临时客串还是疾病信号?1、生理性口臭:临时“小插曲”饮食作祟:大蒜、洋葱、韭菜等含硫化合物的食物,会通过血液循环…

辰亦儒老婆曾之乔回应二胎计划 随缘就好

5月29日,女演员曾之乔出席活动时分享了她的产后生活,表示生完宝宝后感到非常幸福,并透露怀孕期间给儿子取的小名叫“甜蜜”。她还提到与丈夫辰亦儒采取“责任制”方式照顾宝宝,两人会排班负责。对于是否计划要二胎,她表示一切随缘。曾之乔和辰亦儒在2009年合作《爱似百汇》…

kafka学习笔记(三、消费者Consumer使用教程——从指定位置消费)

1.简介 Kafka的poll()方法消费无法精准的掌握其消费的起始位置&#xff0c;auto.offset.reset参数也只能在比较粗粒度的指定消费方式。更细粒度的消费方式kafka提供了seek()方法可以指定位移消费允许消费者从特定位置&#xff08;如固定偏移量、时间戳或分区首尾&#xff09;开…

旅客私自携带230万美元现金入境 折合人民币超1600万元

近日,皇岗海关在福田口岸旅检渠道查获一名旅客违规携带未申报的230万美元现金入境,折合人民币超过1600万元。皇岗海关关员在福田口岸旅检进境大厅对旅客及行李物品进行监管时,发现一名经“无申报通道”通关的旅客携带的行李机检图像异常。随后,该旅客被引导至查验区进一步检…

精度更高、速度更快!从RT-DETR到RF-DETR全面突破实时检测瓶颈

【导读】 YOLO虽快&#xff0c;但其依赖的非最大抑制&#xff08;NMS&#xff09;后处理拖累速度与精度。DETR架构首次实现无需NMS的“一对一”预测&#xff0c;却受限于计算成本。如今&#xff0c;RT-DETR 通过混合编码器、不确定性查询选择等创新突破实时瓶颈&#xff1b;RF…

提升搜索效率:深入了解Amazon Kendra的强大功能

从智能文档搜索到精准的自然语言处理&#xff0c;Amazon Kendra为企业提供了一个强大的解决方案&#xff0c;帮助我们突破传统搜索引擎的局限&#xff0c;快速实现信息的高效整合与检索&#xff0c;接下来让我们一起探索Amazon Kendra如何成为工作中的得力助手&#xff0c;提升…

社群营销:信任比流量值钱

你肯定见过那种群里天天甩链接的&#xff0c;动不动就所有人&#xff0c;点进去全是促销信息——这种玩意儿不叫社群营销&#xff0c;顶多是广告轰炸。 搞社群得先把自己当人&#xff0c;也把别人当人。别整那些机器人自动回复&#xff0c;谁半夜两点发消息都秒回&#xff0c;…