【redis-03】redis缓存穿透、缓存击穿、缓存雪崩

news2024/11/17 1:48:23

redis系列整体栏目


内容链接地址
【一】redis基本数据类型和使用场景https://zhenghuisheng.blog.csdn.net/article/details/142406325
【二】redis的持久化机制和原理https://zhenghuisheng.blog.csdn.net/article/details/142441756
【三】redis缓存穿透、缓存击穿、缓存雪崩https://zhenghuisheng.blog.csdn.net/article/details/142577507

如需转载,请输入:https://blog.csdn.net/zhenghuishengq/article/details/142577507

redis缓存穿透、缓存击穿、缓存雪崩

  • 一,redis缓存穿透、缓存击穿、缓存雪崩
    • 1,缓存击穿(失效)
      • 1.1,造成缓存击穿的原因
      • 1.2,如何解决缓存击穿
        • 1.2.1,添加随机时间
        • 1.2.2,添加限流和降级操作
    • 2,缓存穿透
      • 2.1,什么是缓存穿透
      • 2.2,布隆过滤器
    • 3,缓存雪崩
      • 3.1,缓存雪崩的原因
      • 3.2,如何解决缓存雪崩

一,redis缓存穿透、缓存击穿、缓存雪崩

在使用redis作为缓存时,经常会遇到缓存一系列的问题,如在大型的互联网公司中,一般的组织架构都会比较的依赖redis缓存,因此经常会可能出现以下问题: 缓存穿透、缓存击穿和缓存雪崩

在这里插入图片描述

1,缓存击穿(失效)

1.1,造成缓存击穿的原因

缓存击穿,又被称为缓存失效。如京东网站中,通常会有大量的秒杀场景,通常会分布在不同的时间段做对应的秒杀设计,每个商品都会有对应的秒杀时间,如苹果手机设置18点开始售卖,2个小时后结束。

当然这些商品的商家都需要后台运营进行维护,如选择商品上架、设置过期时间、设置价格等。但是由于数量实在是太多,运营人员一般会选择批量设置操作,假如说新来的运营人员或者说不熟悉这块业务的运营人员来进行这块操作的话,如不小心直接点了一个 一键上架 ,导致所有的商品都启动了秒杀服务,并且设置了1个小时的秒杀时间

在这里插入图片描述

以上是一个简单的架构流程图,假设说在启动了一键上架后,本该在不同时段参与秒杀服务的商品,现在全部都集中的在一个时间段进行了秒杀服务。假设在这1小时内,redis确实可以抗住这段时间内的流量,因为redis做了集群,并且通过内部的多路复用,Reactor模式等,可以暂时支持这段时间的高可用。

由于是批量上架,并且设置的都是1个小时的秒杀,那么其上架时间都一样,那么所有商品的失效时间也一样。当所有的商品都失效之后,那么如果失效时用户没有刷新页面,所有用户都还在浏览商品或者下单商品,那么这些操作都会打到mysql中,mysql的性能肯定是不如redis的,一个mysql能抗住2000-3000的并发量都很不错了,因此这就造成了 缓存击穿,也叫缓存失效

在这里插入图片描述

1.2,如何解决缓存击穿

1.2.1,添加随机时间

既然是大方面的缓存在同一时刻失效的问题导致的缓存击穿,那么首先第一种解决方案就是添加随机的缓存时间,如根据不同的商品设置不同的随机过期时间,如下端代码,在初始化redis的配置类时,添加redis的默认过期时间,并给过期时间加一个0-5分钟之间的随机值

@Configuration
@EnableCaching
public class RedisCacheConfig {

    private static final int BASE_EXPIRE_TIME_SECONDS = 3600; // 1小时
    private static final int RANDOM_EXPIRE_TIME_SECONDS = 300; // 随机抖动最大5分钟

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        // 配置 Redis 缓存的默认过期时间,结合随机化过期时间
        RedisCacheConfiguration cacheConfig = RedisCacheConfiguration.defaultCacheConfig()
                .entryTtl(Duration.ofSeconds(getRandomExpireTime()))  // 设置随机化过期时间
                .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string()))
                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.json()));

        return RedisCacheManager.builder(redisConnectionFactory)
                .cacheDefaults(cacheConfig)
                .build();
    }

    // 生成随机化的缓存过期时间
    private int getRandomExpireTime() {
        Random random = new Random();
        int randomTime = random.nextInt(RANDOM_EXPIRE_TIME_SECONDS); // 生成0到5分钟的随机时间
        return BASE_EXPIRE_TIME_SECONDS + randomTime;
    }

    // 配置默认的Key生成器(可选)
    @Bean
    public SimpleKeyGenerator keyGenerator() {
        return new SimpleKeyGenerator();
    }
}
1.2.2,添加限流和降级操作

在每次对redis进行操作时,对redis进行一个get操作,如果此时发现redis不可用,或者redis获取的值为空,则对用户的请求进行一个限流操作,然后给用户一个友好的提示进行降级,如商品已经卖完,秒杀已经结束等。

除了上面两种方式,还可以在本地添加一个一级缓存操作,在短时间内保证数据的高可用,以至于请求不打到数据库中,给数据库带来压力。

2,缓存穿透

2.1,什么是缓存穿透

如在大型网站中,为了加快整个网站的响应,往往会对一些信息进行缓存操作。如在京东商城中,在查看商品的详细信息时,可能会通过携带商品的id再后台进行查询,或者查看用户的历史订单数据,需要携带用户的id

xxx?id = 10001

但是往往利用这个特点,可以通过抓包的方式获取到一些主要的数据,如拿到用户token,或者直接绕过这些认证鉴权的步骤,直接模拟相关http请求对后台进行数据的拉取和访问。如访问某个类型的商品信息,设置一个随机值,商品信息在redis中找不到,那么此时就会访问数据库,那么最终发现数据库也找不到,这就是缓存穿透

xxx?typeId = 123123232132

当然在这里重点主要是看设置随机id在redis中找不到,然后导致大量数据查mysql,导致加重mysql的负载,进而影响整个系统的吞吐量。

2.2,布隆过滤器

为了解决这种缓存穿透问题,比较靠谱的方案就是使用布隆过滤器来解决。布隆过滤器主要通过一个类似于一个大的bitmap二进制数组,其设计思想主要是通过一些hash函数实现,其核心思想如下:当某个值存在时,这个值不一定存存在;当某个值不存在时,那么这个值一定不存在。在这个二进制数组中,其内部主要有0和1组成,如果数据插入,则在hash的地方设置成1,数组长度可以达到十亿百亿级别。

如以下图,模拟一个10个长度的数组,此时没有数据,其比特位全部设置为0
在这里插入图片描述

比如此时需要设置一个值商品id为10的key,其value值为100

set 10 100

那么在经过布隆过滤器时,就会对这个key值做三次hash计算,如我这边自定义三个hash规则,此时经过hash计算的值为0,1,3。在计算结束之后,需要在布隆过滤器中,对对应的下标设置成1

h1 = 10 % 10 = 0
h2 = (10 * 3 +1) % 10 = 1
h2 = (10 * 5 +3) % 10 = 3    

在这里插入图片描述

在获取商品id为1时,需要再对这个id进行相同的hash,并且需要根据这三个计算出来的hash值与bitmap中的进行比较,判断对应的下下标为的值是否全时1,只有3个全为1的,才认为这个值是可能存在的,那么再去redis缓存中获取数据。只要存在一个值为0,那么就直接认为数据在布隆过滤器中是不存在的

如需要获取id为5的数据,经过相同的hash算法之后,发现计算出来的值分别是5,6,8,然后再去布隆过滤器中进行对比,发现下标为5,6,8的数组对应的值都是0,因此认为这个值是一定不存在的,那么直接返回空即可

h1 = 5 % 10 = 5
h2 = (5* 3 +1) % 10 = 6
h2 = (5* 5 +3) % 10 = 8

假如此时数据来到了1000万,如果还是在redis中查找,那么数据相对会很慢,甚至影响整个系统的性能,但是如果直接使用这种布隆过滤器,是可以大大的加快查询效率的,通过这种布隆过滤器拦截掉大部分的伪造请求,从而降低缓存穿透概率的发生

在这里插入图片描述

部分相关的布隆过滤器的代码如下,定义一个布隆过滤器,然后设置数组大小为10000,hash次数为5,主要有两个方法,一个是将key加入到布隆过滤器的add方法,一个是判断当前key是否在布隆过滤器中的 contains 方法

@Component
public class BloomFilter {
    private final RedisTemplate<String, Object> redisTemplate;
    private final int size = 10000; // 位数组大小
    private final int hashCount = 5; // 哈希函数数量

    @Autowired
    public BloomFilter(RedisTemplate<String, Object> redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    // 使用哈希函数计算索引
    private int[] getHashIndices(String value) {
        int[] indices = new int[hashCount];
        for (int i = 0; i < hashCount; i++) {
            indices[i] = (hash(value, i) % size + size) % size; // 确保索引为正
        }
        return indices;
    }

    // 简单的哈希函数
    private int hash(String value, int seed) {
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            md.update((value + seed).getBytes(StandardCharsets.UTF_8));
            byte[] bytes = md.digest();
            return Math.abs(bytes[0]); // 取哈希值的绝对值
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException(e);
        }
    }

    // 添加元素到布隆过滤器
    public void add(String value) {
        int[] indices = getHashIndices(value);
        for (int index : indices) {
            redisTemplate.opsForValue().setBit("bloom_filter", index, true);
        }
    }

    // 查询元素是否在布隆过滤器中
    public boolean contains(String value) {
        int[] indices = getHashIndices(value);
        for (int index : indices) {
            if (!redisTemplate.opsForValue().getBit("bloom_filter", index)) {
                return false; // 有位为 0,元素一定不存在
            }
        }
        return true; // 所有位均为 1,可能存在
    }
}

3,缓存雪崩

3.1,缓存雪崩的原因

缓存雪崩,指的是因为一个redis出现的一点小问题,导致问题越来越大,从而影响多层架构,就像滚雪球一样,因为一点小错误导致雪球越来越大。

在实际开发中,redis一般都会做主从来保证高可用,做集群来保证高性能。redis集群一般都是可以抗住很高的流量,但是突然集群中因为某个原因导致某个结点挂了,进而让整个集群承受不住那么大流量,进而导致redis集群多个结点挂了,然后数据全部给了mysql,进而导致mysql承受不住那么大流量,进而影响当前服务和其他服务的sql阻塞,进而导致所有tomcat连接池占满,进而导致所有的web服务都不可用。但是导致雪崩的主要原因,还是因为redis的不可用,导致数据全部打到mysql导致的。

除了redis本身不可用之外,也可能因为大key的问题导致,大key值的指的是value特别的大,比如一个用户的数据本来是好好的通过String类型存储的,但是后面优化改成了hash的方式存储,导致这个用户的value值特别大。由于redis的多路复用的特性,以及使用的是单线程的Reactor反应堆模式,导致在加载到内存中以及处理请求比较耗时,导致redis出现了阻塞,让其他命令等待,请求越多导致tomcat连接池占满,进而导致服务被打挂,导致整个服务不可用。

在这里插入图片描述

导致redis不可用或者说不能用的原因有很多,比如外部流量太大把redis集群压垮,或者多个商品在同一时间失效导致redis不能用。缓存雪崩和缓存击穿可以说是非常像,都是因为redis的不可用或者不能用导致请求全部来到了mysql,进而引发整个架构出现不可用的问题。

3.2,如何解决缓存雪崩

  • 首先第一点,得保证redis的高可用,可以搭建多节点cluster集群,或者搭建简单的哨兵集群
  • 设置限流降级功能,请求是先打到web服务,然后再进入redis,如果redis压测是只能 1w/s 的请求,那么在web服务端中得提前进行限流降级功能,当每秒请求达到8000或者9000时,则对多的请求进行一个平滑的降级功能,比如给一个友好的提示页面。
  • 或者用户在进入页面时,将用户的请求加入到mq队列中,然后异步的告知用户前面还有多少人在排队,让用户等待
  • 在上线前对服务以及redis进行压测,对redis进行数据备份,合理的设置jvm服务的个数,mysql的架构等
  • redis内部进行优化,如解决bigkey问题,批量的操作数据,合理的选择数据类型等

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

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

相关文章

如何用IDEA连接HBase

编写java代码&#xff0c;远程连接HBase进行相关的操作 一、先导依赖 代码如下&#xff1a; 二、连接成功

scroll-view滚动条在ios上没有显示滚动条,安卓上显示,亲测有效果

问题描述 微信小程序的scroll-view在ios上没有显示滚动条&#xff0c;但是如果在安卓设备上会显示一个滚动条解决方案 微信小程序只需要在scroll-view上面添加show-scrollbar“{{false}}” enhanced"{{true}}"即可解决 UniApp则修改成:show-scrollbar“false” enh…

图像背景去除的最佳工具和 PNG 网站

PNG 网站是专业人士、设计师和任何需要具有透明背景的高质量图像的人的重要资源。这些网站提供数百万个 PNG&#xff0c;这对于数字项目很有价值。更不用说&#xff0c;这些格式具有保持质量和支持透明度的能力。在这篇文章中&#xff0c;我们将探讨提供无数满足不同需求的 PNG…

产品管理 - 互联网产品(1):产品战略

1、产品方向 即产品目标、目的、方向等。根据人、公司、管理等等因素决定了产品目标有所不同&#xff0c;常见的产品目标有&#xff1a;收入、用户、市场占有率、品牌影响力、资源平衡、财务报表、抛砖引玉、融资规划等 1) 收入 从规划开始就是以赚钱为目的&#xff0c;不管…

SpringBoot(Java)实现MQTT连接(本地Mosquitto)通讯调试

1.工作及使用背景 工作中需要跟收集各种硬件或传感器数据用于Web展示及统计计算分析&#xff0c;如电表、流量计、泵、控制器等物联网设备。 目前的思路及解决策略是使用力控或者杰控等组态软件实现数据的转储&#xff08;也会涉及收费问题&#xff09;&#xff0c;通过组态软件…

【Python语言初识(五)】

一、文件和异常 在Python中实现文件的读写操作其实非常简单&#xff0c;通过Python内置的open函数&#xff0c;我们可以指定文件名、操作模式、编码信息等来获得操作文件的对象&#xff0c;接下来就可以对文件进行读写操作了。这里所说的操作模式是指要打开什么样的文件&#…

软件测评CNAS认可实验室程序文件之检测报告的编制和交付程序

软件测评实验室在申请CNAS认可时&#xff0c;需要根据相关准则文件的要求&#xff0c;建立质量管理体系&#xff0c;其中程序文件是质量管理体系中非常重要的一环。在前面的文章中&#xff0c;我们为大家整体介绍了CNAS软件测评实验室程序文件主要都有哪些&#xff0c;以及对部…

尾巴生活彩虹泥餐盒怎么样?测评10元的国产猫罐头:高爷家、希喂、尾巴生活

我家迎来了一位缅因猫成员&#xff0c;这家伙体型魁梧&#xff0c;颜值爆表&#xff0c;走起路来自带王者风范。说到食量&#xff0c;简直是猫咪界的“大胃王”&#xff0c;一顿饭顶得上四只小猫咪的总和。二三十元一罐的进口罐被它光速炫完&#xff0c;简直是给家里的钱包“瘦…

spring boot文件上传之x-file-storage

spring boot文件上传之x-file-storage 今天看到一个文件上传的开源组件x-file-storage&#xff0c;官方地址如下&#xff1a; https://x-file-storage.xuyanwu.cn/#/ 该组件官网是这样介绍的&#xff0c;如下&#xff1a; 一行代码将文件存储到本地、FTP、SFTP、WebDAV、阿…

细讲 Java 的父子继承、方法的重写与super关键字

&#x1f680; 个人简介&#xff1a;某大型国企资深软件开发工程师&#xff0c;信息系统项目管理师、CSDN优质创作者、阿里云专家博主&#xff0c;华为云云享专家&#xff0c;分享前端后端相关技术与工作常见问题~ &#x1f49f; 作 者&#xff1a;码喽的自我修养&#x1f9…

基于Java开发的(控制台)模拟的多用户多级目录的文件系统

多级文件系统 1 设计目的 为了加深对文件系统内部功能和实现过程的理解&#xff0c;设计一个模拟的多用户多级目录的文件系统&#xff0c;并实现具体的文件物理结构、目录结构以及较为完善的文件操作命令集。 2 设计内容 2.1系统操作 操作命令风格&#xff1a;本文件系统的…

基于单片机的温湿度检测判断系统

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 基于STC89C52单片机&#xff0c;采用dht11温湿度传感器检测温湿度&#xff0c; 通过lcd1602显示屏各个参数&#xff0c;四个按键分别可以增加温湿度的阈值&#xff0c; 如果超过阈值&#xff0c;则…

BaseCTF2024 web

Web [Week1] HTTP 是什么呀 GET: ?basectf%77%65%31%63%25%30%30%6d%65POST: BaseflgX-Forwarded-For:127.0.0.1Referer: BaseCookie: c00k13i cant eat itUser-Agent: Base有Location跳转, 抓包得到flag: QmFzZUNURntkZGUzZjA0Yy1hMDg5LTQwNGMtOTFjNi01ODZjMzAxMzM3Y2J9Cg…

解锁创意新纪元:Stable Diffusion绘画技术的非凡优势

Stable Difusion 是一款从文本到图像的潜在扩散模型&#xff0c;其操作界面如图所示。该模型由初创公司Stabiity A1、慕尼黑大学机器视觉与学习小组以及神经网络视频公司Runway 合作研发&#xff0c;首次发布于2022年8月&#xff0c;而在同年11月更新的2.0版本更是给用户带来了…

“给领导买饭”,刺痛打工人

帮领导办私事&#xff0c;你会接受还是拒绝&#xff1f; 转载&#xff1a;定焦&#xff08;dingjiaoone&#xff09;原创 作者 | 艾乐伊 郑浩钧 苏琦 王璐 编辑 | 苏琦 打工人最讨厌的事&#xff0c;领导喊你帮他带饭带娃&#xff0c;算一件。 近日&#xff0c;上海某教培公司…

fuzzer实战-magma-模糊测试

Getting Started | magma首先打开这个链接&#xff0c;跟着官网指导做&#xff1a; 并且参考Titan的官网使用方法&#xff1a;GitHub - 5hadowblad3/Titan: Research artifact for Oakland (S&P) 2024, "Titan: Efficient Multi-target Directed Greybox Fuzzing&quo…

Rce脚本自动化amp;批量

这里放上一篇我学生的投稿文章 0x00 前言 在现代网络安全领域&#xff0c;远程代码执行&#xff08;RCE&#xff09;漏洞的发现与利用成为了重要的研究课题。随着攻击手段的不断演进&#xff0c;安全专业人士面临着日益复杂的威胁环境。为应对这一挑战&#xff0c;自动化和批…

ChatGPT Sidebar 浏览器插件配置指南

随着聊天机器人技术的不断进步&#xff0c;越来越多的人开始依赖这些强大的工具来提高工作效率、获取信息和解决问题。OpenAI 的 ChatGPT 是其中最受欢迎的聊天机器人之一。为了方便用户在浏览网页时随时与 ChatGPT 互动&#xff0c;开发者们设计了一款名为 ChatGPT Sidebar 的…

30+程序员顶着被裁员的压力,为什么选择从零开始:转行大模型?

在当今这个科技进步迅速的时代&#xff0c;程序员作为引领技术革新的关键角色&#xff0c;正处于一个既充满机会又面临挑战的关键时刻。随着人工智能、大数据处理、云服务等领域的迅猛发展&#xff0c;大型模型&#xff08;例如GPT系列、BERT等&#xff09;已经成为行业内的热议…

【07】纯血鸿蒙HarmonyOS NEXT星河版开发0基础学习笔记-Swiper轮播组件与样式结构重用

序言&#xff1a; 本文详细讲解了关于我们在页面上经常看到的轮播图在鸿蒙开发中如何用Swiper实现&#xff0c;介绍了Swiper的基本用法与属性&#xff0c;及如何面对大段的重复代码进行封装和重用&#xff08;Extend、Styles、Builder&#xff09;&#xff0c;使代码更加简洁易…