如何理解Redis中的缓存雪崩,缓存穿透,缓存击穿?

news2024/10/22 7:55:16

目录

一、缓存雪崩

1.1 解决缓存雪崩问题

二、缓存穿透

2.1 解决缓存穿透

三、缓存击穿

3.1 解决缓存击穿

3.2 如何保证数据一致性问题?


一、缓存雪崩

缓存雪崩是指短时间内,有大量缓存同时过期,导致大量的请求直接查询数据库,从而对数据库造成了巨大的压力,严重情况下可能会导致数据库宕机的情况叫做缓存雪崩。

我们先来看下正常情况下和缓存雪崩时程序的执行流程图,正常情况下系统的执行流程如下图所示:

 缓存雪崩的执行流程如下:

从以上图片我们可以发现,1导致缓存雪崩的主要原因有以下三个:

  1. 缓存过期时间设置不合理 :由于大量缓存数据设置的过期时间相同,导致在某一时刻缓存大量失效,这样就使大量请求直接打到数据库上。
  2. 提供缓存的服务器发生障碍:缓存的服务器出现故障,无法提供缓存服务,那么所有请求就会直接访问数据库。
  3. 缓存数据的热点分布不均:由于是大量缓存直击数据库,所以可能是热点数据分布不均匀,都集中到某个缓存节点上,当这些节点发生故障或者数据失效的时候,会导致请求直接打到数据库。

1.1 解决缓存雪崩问题

 ① 随机生成缓存过期时间

可以避免缓存同时过期

package org.example;

import redis.clients.jedis.Jedis;

import java.util.Random;

public class Main {
    public static void main(String[] args) {
        // 连接到本地 Redis 服务
        Jedis jedis = new Jedis("localhost", 6379);
        //缓存原来的失效时间
        int exTime = 10 * 60;
        //随机数生成
        Random randow = new Random();
        jedis.setex("myKey", exTime+ randow.nextInt(1000), "Hello, Redis!");
        // 关闭连接
        jedis.close();
    }
}

使用多级缓存(成本高,但是也较为主流)

二级缓存指的是除了Redis缓存之外,再设计一个二级缓存,这个二级缓存的过期时间比Redsi中要大一点,当Redis是失效后,先查二级缓存,如果查到数据了,就会直接从二级缓存拿数据返回给前端。不会走数据库,毕竟数据库的资源很宝贵。

这里的本地缓存:可以是mybatis的二级缓存(后两者更为主流,因为mybatis的二级缓存可以存的东西太少了),或者是Google的Guava Cache,Caffeine等。

但是设计二级缓存需要多写很多代码,而且会增加系统的复杂性。虽然查询的时候,走二级缓存没有问题,但是应用程序执行写入操作的时候,那么原本只需要保证Redis里的数据库和数据库里的数据一致即可,但是现在还要保证二级缓存的一致性,数据的一致性更难保证了。

但是Caffeine有方案可以保证本地缓存一致性的问题。

③ 缓存过期前预加载:

在缓存即将过期之前,提前异步加载缓存,避免在缓存失效时大量请求直接打到数据库或者后端服务。

例如看门狗机制,但是实现起来并不简单,因为还需要设置定时任务之类,但是定时任务也有可能会挂,并且也是有一定开销。

④ 开启限流或降级功能:

当缓存发生雪崩时,采用限流或降级的机制来减少服务器的压力,保证系统的可用性。

⑤ 实时监控和预警:

通过监控缓存的状态和命中率,及时发现缓存问题,预警系统管理员或运维人员。

二、缓存穿透

缓存穿透是指查询数据库和缓存都无数据,因为数据库查询无数据,出于容错考虑,不会将结果保存到缓存中,因此每次请求都会去查询数据库,从而给数据库带来了额外的压力,降低了系统性能的情况就叫做缓存穿透。

也就是说缓存穿透是因为数据库查询无数据,出于容错考虑,不会将结果保存到缓存中,因此每次请求都会去查询数据库,这种情况就叫做缓存穿透。

缓存穿透 执行流程如下图所示:

缓存穿透执行流程:Redis 数据库 都被穿透。

2.1 解决缓存穿透

缓存穿透的常见解决方案有以下几个:

1.缓存空对象: 对于查询结果为 nul 或不存在的数据,也可以将它们以特殊值(如"NULL"、特定标识符)进行缓存,并设置较短的过期时间。这样,短时间内相同的查询请求就可以直接从缓存中获得响应,避免了对数据库的直接查询。

2. 布隆过滤器(Bloom Filter): 在请求到达缓存之前,先通过布降过滤器判断数据可能存在还是一定不存在。对于确定不存在的数据,可以直接返回;可能存在则继续査询缓存和数据库。布隆过滤器是一种空间效率极高的概率型数据结构,它会给出“可能存在“或“肯定不存在”的答案。

3. 开启限流功能:当发现大量连接未命中的请求时,可以采用限流策略限制同一时间段内向数据库发送的查询请求数量,减轻数据库压力。

2.2 什么是布隆过滤器? 

布隆过滤器是一种空间效率极高的概率性数据结构,可以用于判断一个元素是否在一个集合中。

它基于位数组和多个哈希函数的原理,可以高效地进行元素的查询,并且占用的空间相对较小,

如下图所示:

这里面存的就是比特,即0或1。根据 key 值计算出它的存储位置,然后将此位置标识全部标识为 1(未存放数据的位置全部为 0),查询时也是查询对应的位置是否全部为 1,如果全部为 1,则说明数据是可能存在的,否则一定不存在.

这种采用存储三个比特的方法,可以有效避免hash冲突,因为如果只存储一个的话,hash冲突可能发生比较频繁。

也就是说,如果布隆过滤器说一个元素不在集合中,那么它一定不在这个集合中;但如果它说一个元素在集合中则有可能是不存在的(存在误差)。 

执行过程

布隆过滤器的具体执行步骤如下:

  1. 在 Redis 中创建一个位数组,用于存储布降过滤器的位向量,每个位置的值设置为 0.
  2. 添加元素到布隆过滤器时,对元素进行多次哈希计算,并将对应的位数组位置设置为 1。
  3. 查询元素是否存在时,对元素进行多次哈希计算,并检查对应的位数组位置是否都为 1,都为1 表示可能存在,其中有一个为 0则一定不存在。

使用场景

布隆过滤器的主要使用场景有以下几个:

  1. 大数据量去重:可以用布降过滤器来进行数据去重,判断一个数据是否已经存在,避免重复插入。
  2. 防止缓存穿透:可以用布隆过滤器来过滤掉恶意请求或请求不存在的数据,避免对后端存储的频繁访问。
  3. 网络爬虫 URL 去重:可以用布降过滤器来判断 URL 是否已经被爬取,避免重复爬取。

三、缓存击穿

缓存击穿指的是某个热点缓存,在某一时刻恰好失效了,然后此时刚好有大量的并发请求,此时这些请求将会给数据库造成巨大的压力,这种情况就叫做缓存击穿。缓存击穿的执行流程如下图所示:

主要原因是: 热点数据再缓存中失效或淘汰,并发请求同时访问该数据库,导致缓存无法命中。

缓存击穿的执行流程:(Redis 被击穿)

3.1 解决缓存击穿

① 设置永不过期:

对于某些热点缓存,我们可以设置永不过期,这样就能保证缓存的稳定性,但需要注意在数据更改之后,要及时更新此热点缓存,不然就会造成查询结果的误差。

② 缓存过期前预加载

在缓存即将过期之前,提前异步加载缓存,避免在缓存失效时大量请求直接打到数据库或者后端服务。

例如看门狗机制,但是实现起来并不简单,因为还需要设置定时任务之类,但是定时任务也有可能会挂,并且也是有一定开销。

③ 使用多级缓存:

可以使用多级缓存架构,将热门数据同时缓存在多个缓存节点上,避免单一节点故障导致请求直接访问数据库或者后端服务,例如可以设计多级缓存,也就是使用分布式缓存(Redis)+本地缓存(Caffeine/Guava Cache),如下图所示:

但是设计二级缓存需要多写很多代码,而且会增加系统的复杂性。虽然查询的时候,走二级缓存没有问题,但是应用程序执行写入操作的时候,那么原本只需要保证Redis里的数据库和数据库里的数据一致即可,但是现在还要保证二级缓存的一致性,数据的一致性更难保证了。

但是Caffeine有方案可以保证本地缓存一致性的问题。

④ 开启限流或降级功能:

当缓存发送雪崩时,采用限流或降级的机制来减少服务器的压力,保证系统的可用性。

3.2 如何保证数据一致性问题?

如何保证本地缓存的一致性?
在分布式系统中,使用本地缓存最大的问题就是一致性问题,所谓的一致性问题指的是当数据库发生数据变更时缓存也要跟着一起变更。而分布式系统中每台机器都有自己的本地缓存,所以想要保证(本地缓存的)一致性是个比较难的问题,但通过以下手段可以最大程度的保证本地缓存的一致性问题:

  1. 本地缓存失效时间尽量短,短的存活周期,保证了尽可能的保证了一致性。
  2. 通过微服务中的配置中心(例如 Nacos)来协调,因为所有服务器都连接的配置中心,所以当数据修改之后可以给配置中心推送一个配置修改的信息,然后配置中心再把变更信息推送给各个服务,服务订阅到配置变更消息之后,就会更新自己的本地缓存,这样就实现了数据的一致性。
  3. 使用缓存框架的自动更新功能,例如 Caffeine 中的 refresh 功能自动刷新缓存。

不同的业务系统,会采用不同的解决方案,例如以下这些场景和对应的解决方案:

  • 如果对数据一致性要求不是很高,并且程序的并发压力不大的情况下,可能使用方案 1,也就是设置本地缓存短时间内失效的解决方案,因为它的实现最简单。
  • 如果对数据的一致性要求极高,且有配置中心的情况下,可使用配置中心协调和同步本地缓存。
  • 相反,如果对一致性要求没有那么高,且为高并发的系统,那么可以采用本地缓存的自动更新功能来实现。

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

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

相关文章

HTTP协议(请求方式,响应方式,请求行、头、体,状态码)是热点面试题【详解】

目录 1. HTTP简介 1.介绍 2.浏览器抓包 3.特点 2. HTTP请求 1.HTTP请求的格式 2.HTTP请求方式 3.GET方式的请求示例 请求行 请求头 请求体 4.POST方式的请求示例 请求行 请求头 请求体 GET和POST的区别 5.HTTP响应 1.HTTP响应的格式 2 常见响应头 3 响应…

python基础(11)《Allure报告中的组件用法》

使用 官方教程:https://docs.qameta.io/allure 入门 想要看到allure报告,需要做2个步骤: 1、pytest执行时关联allure:pytest命令带上--alluredir 结果存放目录或--alluredir结果存放目录; 2、打开执行报告&#xff…

前端性能优化 | CDN缓存

前言 CDN(Content Delivery Network)是一种分布式的网络架构,通过在全球各地部署节点服务器来快速传输和分发网络内容。CDN的主要目标是提供快速、可靠的内容传输,以提升用户体验。 本文主要从以下方面讲解CDN 什么是CDNCDN的作…

利用GPT开发应用003:GPT分词和预测

文章目录 一、概率问题二、令牌(分词)三、预测 一、概率问题 像 GPT 这样的大型语言模型接收一个提示,并返回通常在上下文中有意义的输出。例如,提示可以是“今天天气很好,所以我决定”(“The weather is n…

vite项目修改node_modules

问题详情 在使用某个依赖的时候遇到了bug,提交issue后不想一直等待到作者更新版本,所以寻求临时自己解决 问题解决 在node_modules里找到需要修改的依赖,修改想要修改的代码 修改后记得保存 然后在node_modules里找到.vite文件夹&#x…

便捷在线导入:完整Axure元件库集合,让你的设计更高效!

Axure元件库包含基本的工具组件,可以使原型绘制节省大量的重复工作,保持整个设计页面的一致性和标准化,同时显得专业。Axure元件库就像我们日常生活中的门把手、自行车踏板和桌子上的螺丝钉,需要组装才能使用。作为一名成熟的产品…

java集合(泛型数据结构)

1.泛型 1.1泛型概述 泛型的介绍 泛型是JDK5中引入的特性&#xff0c;它提供了编译时类型安全检测机制 泛型的好处 把运行时期的问题提前到了编译期间 避免了强制类型转换 泛型的定义格式 <类型>: 指定一种类型的格式.尖括号里面可以任意书写,一般只写一个字母.例如: …

职工医疗报销管理系统

目录 1 系统目标与范围说明... 0 1.1项目名称... 0 1.2问题说明... 0 1.3项目目标... 0 1.4项目范围... 0 1.5初步想法... 0 1.6可行性研究计划... 0 2 可行性分析报告... 1 2.1系统概述... 1 2.2可行性分析... 2 2.3结论意见... 2 3 项目开发计划... 2 3.1系统…

【笔记】Android Telephony 漫游SPN显示定制(Roaming Alpha Tag)

一、功能名词简介和显示规则 Alpha Tag&#xff1a;运营商名称标识符&#xff0c;也是用于标识运营商的一个名称。客户需求描述常用名词&#xff0c;对开发而言都是SPN/PLMN功能模块的内容&#xff0c;状态栏左上角的运营商名称显示。 SPN相关文章&#xff1a; 【笔记】SPN和…

Java on VS Code 2月更新|创建 Maven 模块支持,项目管理体验优化!

作者&#xff1a;Nick Zhu - Senior Program Manager, Developer Division At Microsoft 排版&#xff1a;Alan Wang 大家好&#xff0c;欢迎来到2024年2月的 Visual Studio Code Java 更新&#xff01;在本篇博客中&#xff0c;我们将分享项目管理体验的改进以及 Maven 多模块…

【MySQL | 第三篇】MySQL索引及两种索引分类方法总结

文章目录 3.MySQL索引及两种索引分类方法3.1索引的概念3.1.1相关定义3.1.2查询例子 3.2索引的底层3.2.1二叉树&#xff08;1&#xff09;满二叉树&#xff08;2&#xff09;完全二叉树&#xff08;3&#xff09;二叉查找树&#xff08;4&#xff09;二叉平衡树&#xff08;AVL&…

uniapp——nextTick(vue3)数据更新完之后加载

说明 将回调推迟到下一个 DOM 更新周期之后执行。在更改了一些数据以等待 DOM 更新后立即使用它。 代码 <view class"tabBox"><scroll-view scroll-x"true" :scroll-with-animation"true"><view class"box"><…

利用“定时执行专家”循环执行BAT、VBS、Python脚本——含参数指定功能

目录 一、软件概述 二、VBS脚本执行设置 三、触发器设置 四、功能亮点 五、总结 在自动化办公和日常计算机任务管理中&#xff0c;定时执行脚本是一项非常重要的功能。今天&#xff0c;我将为大家带来一款名为“定时执行专家”的软件的评测&#xff0c;特别是其定时执行VB…

leetCode刷题 5.最长回文子串

目录 1. 思路 2. 解题方法 3. 复杂度 4. Code 题目&#xff1a; 给你一个字符串 s&#xff0c;找到 s 中最长的回文子串。 如果字符串的反序与原始字符串相同&#xff0c;则该字符串称为回文字符串。 示例 1&#xff1a; 输入&#xff1a;s "babad" 输出&#x…

3.7 day2 Free RTOS

使用ADC采样光敏电阻数值&#xff0c;如何根据这个数值调节LED灯亮度。2.总结DMA空闲中断接收数据的使用方法 while (1){/* USER CODE END WHILE *//* USER CODE BEGIN 3 */adc_value HAL_ADC_GetValue(&hadc);TIM3->CCR3 adc_value * 999 / 4095;printf("%d …

Docker网络+原理+link+自定义网络

目录 一、理解Docker网络 1.1 运行tomcat容器 1.2 查看容器内部网络地址 1.3 测试连通性 二、原理 2.1 查看网卡信息 2.2 再启动一个容器测试网卡 2.3 测试tomcat01 和tomcat02是否可以ping通 2.4 只要删除容器,对应网桥一对就没了 2.5 结论 三、--link 3.…

探索考古文字场景,基于YOLOv7【tiny/l/x】不同系列参数模型开发构建文本考古场景下的甲骨文字符图像检测识别系统

甲骨文是一种非常历史悠久的古老文字&#xff0c;在前面我们基本上很少有涉及这块的内容&#xff0c;最近正好在做文字相关的项目开发研究&#xff0c;就想着基于甲骨文的场景来开发对应的检测识别系统&#xff0c;首先看下实例效果&#xff1a; YOLOv7是 YOLO 系列最新推出的Y…

dubbo3适配springboot2.7.3

版本详细 <dependency><groupId>org.apache.dubbo</groupId><artifactId>dubbo</artifactId><version>3.0.3</version> </dependency><parent><groupId>org.springframework.boot</groupId><artifactId&…

css 用flex做成田字型

哈喽&#xff0c;各位小伙伴&#xff01;今天给大家来css控制div完成田字型样式&#xff0c;来&#xff0c;看看下面的效果图&#xff1a; 一看就知道你们想要代码了&#xff0c;不急。代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head>&…

c++ 11 新特性 不同数据类型之间转换函数之reinterpret_cast

一.不同数据类型之间转换函数reinterpret_cast介绍 reinterpret_cast是C中的一种类型转换操作符&#xff0c;用于执行低级别的位模式转换。具体来说&#xff0c;reinterpret_cast可以实现以下功能&#xff1a; 指针和整数之间的转换&#xff1a;这种转换通常用于在指针中存储额…