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

目录

一、缓存雪崩

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 是否已经被爬取,避免重复爬取。

布隆过滤器会不会比较耗内存,添加数据怎么办,删除数据怎么办

布隆过滤器是一种用于快速判断一个元素是否存在于集合中的数据结构,它的优点是查询效率高且占用内存较少。然而,布隆过滤器也存在一些限制和操作上的考虑。

  1. 内存消耗:布隆过滤器的内存消耗主要取决于预期的误判率和要存储的元素数量。误判率越低,所需的内存空间就越大。一般情况下,布隆过滤器的内存消耗相对较小,但随着元素数量的增加,内存占用也会逐渐增加。
  2. 添加数据:向布隆过滤器添加数据时,需要对元素进行哈希计算,并将对应的位标记为1。如果哈希冲突较多,可能会导致位的重复标记,进而影响误判率。在添加数据时,可以适当调整布隆过滤器的容量和哈希函数的数量,以平衡误判率和内存消耗。
  3. 删除数据:布隆过滤器本身不支持直接删除已添加的元素,因为删除一个元素可能会影响其他元素的判断结果。如果需要删除元素,一种常见的做法是使用计数器或其他数据结构辅助记录元素的添加次数,然后在判断元素是否存在时,根据计数器的值进行判断。当计数器为0时,可以认为元素不存在。

需要注意的是,布隆过滤器在判断元素是否存在时,可能存在一定的误判率。因此,在使用布隆过滤器时,需要根据具体的应用场景和需求来选择合适的误判率和内存消耗。

三、缓存击穿

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

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

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

3.1 解决缓存击穿

① 设置永不过期:

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

② 缓存过期前预加载

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

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

③ 使用多级缓存:

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

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

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

④ 开启限流或降级功能:

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

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

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

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

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

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

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

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

相关文章

三维卡通数字人解决方案,支持unity和Maya

企业对于品牌形象和宣传手段的创新需求日益迫切,美摄科技凭借其在三维卡通数字人领域的深厚积累,推出了面向企业的三维卡通数字人解决方案,帮助企业轻松打造独具特色的虚拟形象,提升品牌影响力和市场竞争力。 美摄科技的三维卡通…

Postman请求API接口测试步骤和说明

Postman请求API接口测试步骤 本文测试的接口是国内数智客(www.shuzike.com)的API接口手机三要素验证,验证个人的姓名,身份证号码,手机号码是否一致。 1、设置接口的Headers参数。 Content-Type:applicati…

Kafka消费者重平衡

「(重平衡)Rebalance本质上是一种协议,规定了一个Consumer Group下的所有Consumer如何达成一致,来分配订阅Topic的每个分区」。 比如某个Group下有20个Consumer实例,它订阅了一个具有100个分区的Topic。 正常情况下&…

【2024.03.12】定时执行专家 V7.2 发布 - TimingExecutor V7.2 Release

目录 ▉ 软件介绍 ▉ 新版本 V7.2 下载地址 ▉ V7.2 新功能 ▼2024-03-12 V7.2 - 更新日志 ▉ V7.x 新UI设计 ▉ 软件介绍 《定时执行专家》是一款制作精良、功能强大、毫秒精度、专业级的定时任务执行软件。软件具有 25 种【任务类型】、12 种【触发器】触发方式&#x…

【C++算法模板】图的存储-邻接矩阵

文章目录 邻接矩阵洛谷3643 图的存储 邻接矩阵 邻接矩阵相比于上一篇博客邻接表的讲解要简单得多 数据结构,如果将二维数组 g g g 定义为全局变量,那默认初始化应该为 0 0 0 ,如果题目中存在自环,可以做特判, m e …

因为manifest.json文件引起的 android-chrome-192x192.png 404 (Not Found)

H5项目打包之后,总是有这个报错,有时候还有别的icon也找不见 一通调查之后,发现是因为引入了一个vue插件 这个插件引入之后,webpack打包的时候就会自动在dist文件夹中产生一个manifest.json文件这个文件里面主要就是一些icon地址的…

制作图片马:二次渲染(upload-labs第17关)

代码分析 $im imagecreatefromjpeg($target_path);在本关的代码中这个imagecreatefromjpeg();函数起到了将上传的图片打乱并重新组合。这就意味着在制作图片马的时候要将木马插入到图片没有被改变的部分。 gif gif图的特点是无损,我们可以对比上传前后图片的内容…

【问题解决】VMWare虚拟机主IP地址:网络信息不可用

在今天想用man命令查看相关函数的帮助时,突然发现自己的XShell连接不上虚拟机,通过在终端使用ifconfig命令也只查看得到本地回环,虽然最近常遇到这个问题,但一般通过重启虚拟机就能得以解决。 不过这次重启也不奏效了&#xff0c…

Java输出流之BufferWriter类

咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java SE相关知识点了,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好…

爬虫入门到精通_框架篇14(PySpider架构概述及用法详解)

官方文档 Sample Code: from pyspider.libs.base_handler import *class Handler(BaseHandler):crawl_config {}# minutes24 * 60:每隔一天重新爬取every(minutes24 * 60)def on_start(self):self.crawl(http://scrapy.org/, callbackself.index_page)…

【C语言】Leetcode 66. 加一

文章目录 题目思路代码呈现 题目 链接: link 思路 题目的意思是把一个数以数组的形式输入,然后在这个书的末尾加一,应该是要考察,如果数加一后位数发生变化,比如9991成1000,3位数变成4位数怎么处理的。 并且我们可以…

python调用clickhouse

(作者:陈玓玏) 使用clickhouse-driver包,先通过pip install clickhouse-driver安装包,再通过以下代码执行sql。 from clickhouse_driver import Client client Client(host10.43.234.214, port9000, userclickhou…

主流开发语言和开发环境、程序员如何选择职业赛道?

🌟 前言 欢迎来到我的技术小宇宙!🌌 这里不仅是我记录技术点滴的后花园,也是我分享学习心得和项目经验的乐园。📚 无论你是技术小白还是资深大牛,这里总有一些内容能触动你的好奇心。🔍 &#x…

C++开发基础——IO操作与文件流

一,基础概念 C的IO操作是基于字节流,并且IO操作与设备无关,同一种IO操作可以在不同类型的设备上使用。 C的流是指流入/流出程序的字节序列,在输入操作中数据从外部设备(键盘,文件,网络等)流入程序&#x…

12 list的使用

文档介绍 文档介绍 1.list是可以在常数范围内的任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代 2.list的底层是带头双向链表循环结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和…

大话设计模式 :UML类图 原版部分

目录 原书部分总结各符号继承关系实现接口关联关系聚合关系组合关系依赖关系 原书部分 总结 各符号 ‘’ 表示public ‘-’ 表示private ‘#’表示protected 棒棒糖表示法 类内实现的接口 用棒棒糖的形状在外部具体实现 继承关系 实现接口 关联关系 聚合关系 组合关系 依赖…

Redis核心数据结构之压缩列表(二)

压缩列表 压缩列表节点的构成 encoding 节点的encoding属性记录了节点的content属性所保存数据的类型及长度: 1.一字节、两字节或者五字节长,值得最高位为00、01或者10的是字节数组编码:这种编码表示节点的content属性保存着字节数组,数组的长度由编…

小迪安全39WEB 攻防-通用漏洞CSRFSSRF协议玩法内网探针漏洞利用

#知识点: 逻辑漏洞 1、CSRF-原理&危害&探针&利用等 2、SSRF-原理&危害&探针&利用等 3、CSRF&SSRF-黑盒下漏洞探针点 #详细点: CSRF 全称:Cross-site request forgery,即,跨站请求…

ThingsBoard开源物联网平台介绍

1. Thingsboard 简介 ThingsBoard是一个基于Java的开源物联网平台,旨在实现物联网项目的快速开发、管理和扩展。它使用行业标准的物联网协议(MQTT、CoAP和HTTP)实现设备连接,并支持云和本地部署。ThingsBoard结合了可扩展性、容错…

Springboot+vue的疫情居家办公系统(有报告)。Javaee项目,springboot vue前后端分离项目。

演示视频: Springbootvue的疫情居家办公系统(有报告)。Javaee项目,springboot vue前后端分离项目。 项目介绍: 采用M(model)V(view)C(controller&#xff09…