【Redis 6】缓存穿透、缓存雪崩、缓存击穿(附解决方案、代码)

在这里插入图片描述

  各位小伙伴们大家好,欢迎来到这个小扎扎的Redis 6专栏,在这个系列专栏中我对B站黑马的Redis教程进行一个总结,鉴于 看到就是学到、学到就是赚到 精神,这波依然是血赚 ┗|`O′|┛

💡Redis知识点速览

  • 🍖 缓存穿透
    • 🥩 原理以及解决方案
    • 🥩 缓存空对象代码实现
  • 🍖 缓存雪崩
    • 🥩 原理以及解决方案
  • 🍖 缓存击穿
    • 🥩 原理以及解决方案
    • 🥩 互斥锁代码实现
    • 🥩 逻辑过期代码实现

🍖 缓存穿透

🥩 原理以及解决方案

  缓存穿透是指客户端请求的数据在缓存和数据库中都不存在,这样请求就会先访问缓存再打到数据库。这原本是个正常现象并没有什么事情,但是如果有人借助这个漏洞一直请求缓存和数据库中都不存在的数据,那么就极有可能导致数据库崩溃。

  常见的解决方案有两种:第一种就是使用缓存空对象,也就是当请求发送过来缓存和数据库中都没有数据的时候,将该请求的结果为null并设置过期时间存入缓存中,然后返回401状态码,等到下一次请求时直接返回null即可。这样做的优点就是实现简单方便维护,但是造成额外内存损耗的缺点也很明显,过期时间就可以降低该缺点的影响

  第二种方案就是在缓存查询之前使用布隆过滤器,布隆过滤器就是由byte数组和一系列哈希函数两部分组成的数据结构,将数据使用hash函数计算出hash值,然后将这个hash值转成二进制位保存至布隆过滤器中,请求发送过来的话就计算出它的hash值,对应位置为1就说明存在0就是不存在。布隆过滤器的优点是不用频繁添加缓存内存占用小,但是缺点是实现相对复杂,而且会出现误判,布隆过滤器判断不存在的值一定不存在,它判断存在的值不一定就存在

🥩 缓存空对象代码实现

  缓存空对象和之前的查询相比无非就是两步,一是缓存和数据库中都查不到的话就往缓存中添加然后返回错误信息,二是缓存中查到数据进行非空判断,如果是""串的话就刷新TTL然后返回错误信息,下面的代码中的15~19行 22~26行中分别有体现

/*** 根据id查询商铺信息,涉及到redis的缓存* @param id  商铺id* @return 前端返回信息*/
@Override
public Result queryById(Long id) {// 从redis查询商铺缓存String shopJson = stringRedisTemplate.opsForValue().get(RedisConstants.CACHE_SHOP_KEY + id);// 判断该商铺缓存中是否存在if (StrUtil.isNotBlank(shopJson)) {// 存在直接返回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return Result.ok(shop);} else if ("".equals(shopJson)) {// 缓存中存在但是结果为""空字符串 也就是说之前使用 缓存空对象 方案时存入的,这样的话就刷新它的缓存时间返回异常stringRedisTemplate.expire(RedisConstants.CACHE_SHOP_KEY + id, RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);return Result.fail("店铺不存在");}// 不存在查询数据库Shop shop = getById(id);if (shop == null) {// 数据库中不存在 将null存入缓存设置过期时间2min并返回错误信息stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id, "", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);return Result.fail("店铺不存在");}// 数据库中存在写入redisstringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id, JSONUtil.toJsonStr(shop), RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);// 返回return Result.ok(shop);
}

在这里插入图片描述

🍖 缓存雪崩

🥩 原理以及解决方案

  缓存雪崩是指在同一时段大量的缓存key同时失效或者Redis服务宕机,导致大量请求到达数据库,因此会给数据库带来巨大压力
  为了解决大量缓存数据同时失效,可以将数据的TTL设置为随机值,而不是使用一个固定值。为了解决Redis服务宕机,可以使用主从架构的集群提高服务的可用性,万一出现宕机可以使用从节点顶上。为了进一步防止缓存雪崩,我们还可以给缓存业务添加降级限流策略,也就是说当redis发生故障的时候可以直接拒绝服务而不是继续访问数据库;或者给业务添加多级缓存,在浏览器、nginx、redis、jvm、数据库等一层层的添加缓存

🍖 缓存击穿

🥩 原理以及解决方案

  缓存击穿也叫热点Key问题,就是一个被高并发访问并且缓存重建业务较复杂的key突然失效了,此时很多的请求会在瞬间给数据库带来巨大冲击。

  为了解决缓存击穿可以使用互斥锁,如果发生缓存击穿后,第一个请求查询数据库中该数据的时候,使用一个锁锁住,后续的所有请求在锁未放开之前访问这个数据就让它休眠一会重新查询缓存。这个方案优点就不说了,缺点就是在第一个线程写缓存期间,其他访问该数据的线程拿不到锁就只能处于等待状态,所以说这就很损耗性能

  还有一种方案就是逻辑过期,顾名思义逻辑过期就是不作真正的删除,而是使用一个字段存储过期时间代替TTL的过期删除,所有的线程在获取到数据的时候都去通过过期时间字段判断是否过期,过期的话就新建一个线程先更新数据库再删除缓存,自己就返回已过期的数据,在此期间所有的访问都会返回过期数据,等到新建线程的任务完成之后再次访问的线程就负责添加新的缓存数据并返回新的数据

🥩 互斥锁代码实现

  互斥锁方案解决缓存击穿相比较于缓存空对象解决缓存穿透的方案而言,最大的不同就是,在从缓存中查询到数据的情况下,需要先判断一下是否可以获得该数据对应的锁,可以就查询数据库并写入缓存,否则线程休眠重新调用该方法,上述代码需要放在try catch中使用finally释放锁,以上思路在21~40代码中实现

/*** 查询商铺信息 互斥锁解决缓存击穿* @param id  商铺编号* @return 查询到的商铺信息*/
public Shop queryWithMutex(Long id) {// 从redis查询商铺缓存String shopJson = stringRedisTemplate.opsForValue().get(RedisConstants.CACHE_SHOP_KEY + id);// 判断该商铺缓存中是否存在if (StrUtil.isNotBlank(shopJson)) {// 存在直接返回Shop shop = JSONUtil.toBean(shopJson, Shop.class);return shop;} else if ("".equals(shopJson)) {// 缓存中存在但是结果为""空字符串 也就是说之前使用 缓存空对象 方案时存入的,这样的话就刷新它的缓存时间返回异常stringRedisTemplate.expire(RedisConstants.CACHE_SHOP_KEY + id, RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);return null;}// 不存在先查看是否可以获取到锁,如果可以就休眠重试,否则就查询数据库写缓存Shop shop = null;try {if (!tryLock(RedisConstants.LOCK_SHOP_KEY + id)) {// 获取锁失败Thread.sleep(50);queryWithMutex(id);}shop = getById(id);if (shop == null) {// 数据库中不存在 将null存入缓存设置过期时间2min并返回错误信息stringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id, "", RedisConstants.CACHE_NULL_TTL, TimeUnit.MINUTES);return null;}// 数据库中存在写入redisstringRedisTemplate.opsForValue().set(RedisConstants.CACHE_SHOP_KEY + id, JSONUtil.toJsonStr(shop), RedisConstants.CACHE_SHOP_TTL, TimeUnit.MINUTES);} catch (InterruptedException e) {throw new RuntimeException(e);} finally {// 释放互斥锁unLock(RedisConstants.LOCK_SHOP_KEY + id);}// 返回return shop;
}

🥩 逻辑过期代码实现

  逻辑过期的前提条件是先向缓存中存入一个带有过期时间字段的商铺信息,也就是模拟现将需要做活动的商品信息存入到redis缓存中,然后再对缓存进行查询,如果缓存中不存在就返回null,存在的话就进行逻辑过期的操作

// 创建线程池 用于更新缓存时间过期的时候 更新缓存使用
private static final ExecutorService CACH_REBUILD_EXECUTOR = Executors.newFixedThreadPool(5);/*** 查询商铺信息 逻辑过期解决缓存击穿* @param id  商铺编号* @return 查询到的商铺信息*/
public Shop queryWithLogicalExpire(Long id) {// 从redis查询商铺缓存String shopJson = stringRedisTemplate.opsForValue().get(RedisConstants.CACHE_SHOP_KEY + id);// 判断该商铺缓存中是否存在if (StrUtil.isBlank(shopJson)) {// 不存在直接返回nullreturn null;}// 存在 反序列化获取 店铺信息 和 expire字段RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);Shop shop = JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class);LocalDateTime expireTime = redisData.getExpireTime();// 判断是否过期 过期时间在当前时间之后即为未过期if (expireTime.isAfter(LocalDateTime.now())) {// 未过期直接返回店铺信息return shop;}// 已过期 尝试获取锁if (tryLock(RedisConstants.LOCK_SHOP_KEY + id)) {// 获取锁成功 开启独立线程去做缓存重建CACH_REBUILD_EXECUTOR.submit(() -> {// 创建缓存try {saveShopWithExpireTimeToRedis(id, 20L);} catch (Exception e) {throw new RuntimeException(e);} finally {// 释放锁unLock(RedisConstants.LOCK_SHOP_KEY + id);}});}// 返回return shop;
}

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

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

相关文章

8软件遇到的问题及解决方法_Excel工作表中的8个常见问题,你一定遇到过,附解决方法...

在Excel工作表中,最常用的还是一些技巧,如果能够熟练掌握,对于工作效率的提高绝对不是一点点哦,结合工作实际,小编对工作中常见的问题进行了总结,一共有8类,你一定也遇到过……一、Excel工作表常…

unity着色器和屏幕特效开发秘笈_Oculus研发分享:开发移动VR内容时应避免的PC渲染技术...

查看引用/信息源请点击:映维网开发移动VR内容时应避免的PC渲染技术(映维网 2019年11月25日)有不少开发者都是以与PC相同的方式来开发Quest游戏,但这可能会导致优化性能方面出现大量困难。Oculus软件工程师特雷弗达什(T…

Java包装类、java中的方法传参机制:按值调用

各位小伙伴们大家好,欢迎来到这个小扎扎的《Java核心技术 卷Ⅰ》笔记专栏,在这个系列专栏中我将记录浅学这本书所得收获,鉴于 看到就是学到、学到就是赚到 精神,这波简直就是血赚 💡涉及的知识点速通🛫 方法…

尤克里里怎么样_尤克里里和吉他区别?尤克里里与吉他相比有什么不可替代的优势...

尤克里里和吉他有什么区别?想必大家都见过尤克里里吧,就是类似吉他形状的一种小型弦拨乐器。我们可以简单的认为:尤克里里是简化版本的吉他,更加简单,更加便宜。小编弹的就是尤克里里2.从外观上:吉他很大&a…

饿汉懒汉单例设计模式的使用及区别、java中的import关键字

各位小伙伴们大家好,欢迎来到这个小扎扎的《Java核心技术 卷Ⅰ》笔记专栏,在这个系列专栏中我将记录浅学这本书所得收获,鉴于 看到就是学到、学到就是赚到 精神,这波简直就是血赚 💡涉及的知识点速通🛫 关于…

广电运通不好进吗_我可以说郑州新风的安装大部分都是垃圾吗?

说郑州的新风安装都是垃圾,这话很无礼,很自大,很傲慢,但是我能说确实是这样嘛?其实包括我以前安装的也不合格——虽然我不是故意的。这几年见过许多家同行安装的新风,可以说目前见到的很多家都不合格&#…

Java迭代器和Collection接口

各位小伙伴们大家好,欢迎来到这个小扎扎的《Java核心技术 卷Ⅰ》笔记专栏,在这个系列专栏中我将记录浅学这本书所得收获,鉴于 看到就是学到、学到就是赚到 精神,这波简直就是血赚 💡涉及的知识点速通🛫 关于…

私钥经过哈希计算可以产生公钥_「区块链基础概念100」:公钥和私钥 | 027

免责声明:本文旨在传递更多市场信息,不构成任何投资建议。文章仅代表作者观点,不代表火星财经官方立场。小编:记得关注哦投资区块链,猛戳:火星财经App下载来源:学习区块链原文标题:「…

关于List集合类ArrayList、LinkedList、Vector详解

各位小伙伴们大家好,欢迎来到这个小扎扎的《Java核心技术 卷Ⅰ》笔记专栏,在这个系列专栏中我将记录浅学这本书所得收获,鉴于 看到就是学到、学到就是赚到 精神,这波简直就是血赚 💡涉及的知识点速通🛫 关于…

关于Set集合类你都知道什么?来自《卷Ⅰ》的灵魂提问

各位小伙伴们大家好,欢迎来到这个小扎扎的《Java核心技术 卷Ⅰ》笔记专栏,在这个系列专栏中我将记录浅学这本书所得收获,鉴于 看到就是学到、学到就是赚到 精神,这波简直就是血赚 💡涉及的知识点速通🛫 关于…

流程图虚线框表示什么_UI设计|APP的交互线框布局设计

一.流程图设计流程图(Flow Chart):用图示的方式反映出特定主体为了满足特定需求而进行的有特定逻辑关系的一系列操作过程。流程图的四种基本结构:顺序结构,条件结构(又称选择结构),循环结构,分支结构。1.流程图的常用符…

使用Redis完成商品秒杀业务

各位小伙伴们大家好,欢迎来到这个小扎扎的Redis 6专栏,在这个系列专栏中我对B站黑马的Redis教程进行一个总结,鉴于 看到就是学到、学到就是赚到 精神,这波依然是血赚 ┗|`O′|┛ 💡Redis知识点速览&#…

表格列隐藏_【excel每日提升】Excel隐藏列,不让别人打开!

【新朋友】点击标题下面蓝色字“王俊东“关注。 【老朋友】点击右上角,转发或分享本页面内容。excel系列课程excel特效系列课程开始了,今天第2节!第1节:Excel有公式的单元格标记颜色,很简单!第2节&#xf…

“毕业季”|一个java开发实习生的OFFER之路

哈喽哈喽大家好,这里是小扎扎的博客。相信有关注过我的好盆友们可能会发现我已经有一段时间没有出来划水了,那么这段时间小扎扎都在干什么呢?没错!我确实是去找实习了!接下来就给大家介绍一下本次战役的战况如何 活动地…

virtualbox 该内存不能为written_系统提示“该内存不能为read”的原因和解决办法...

我们单位的电脑经常显示这个对话框,已经有好几年了,单位的老头们都不怎么懂电脑,我本人也不爱管闲事。但是出现这种对话框的原因是什么呢?又怎么解决呢?一般电脑经常出现蓝屏和死机,而且频繁出现。有时会出…

插件properties_Mybatis3系列 - 4. mybatis-config的properties属性详解

前两章简单的讲解了MyBatis的使用方式. 接下来先全局的说一下MyBatis的全局的xml配置详细说明.XML格式定义-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">configuration 的映射文件(根据定义顺序说明)properties 属性…

【Redis 6】秒杀业务——分布式锁

各位小伙伴们大家好,欢迎来到这个小扎扎的Redis 6专栏,在这个系列专栏中我对B站黑马的Redis教程进行一个总结,鉴于 看到就是学到、学到就是赚到 精神,这波依然是血赚 ┗|`O′|┛ 💡Redis知识点速览&#…

【Swagger】看这一篇就够了

各位小伙伴们大家好,欢迎跟着小扎扎一起学习【Swagger】这门技术,在本片博客中我对B站狂神的Swagger教程进行一个总结,鉴于 看到就是学到、学到就是赚到 精神,这波依然是血赚 ┗|`O′|┛ 💡Swagger知识点…

用python画大雄_python制作斗图生成器

网上各种带文字的表情图片都被大家玩坏了,今天就和大家一起用 python 亲自做一个带字表情图片生成器。 不知道大家有没有看到网上有很多人都在说 "人生苦短,我用 python",这句话我之前也不是很理解,觉得人生苦短和用pyt…

【spring cloud】(一)使用idea创建可相互调用的多模块应用

各位小伙伴们大家好,欢迎来到这个小扎扎的spring cloud专栏,在这个系列专栏中我对B站尚硅谷阳哥的spring cloud教程进行一个总结,鉴于 看到就是学到、学到就是赚到 精神,这波依然是血赚 ┗|`O′|┛ 💡spr…