赞
踩
目录
缓存雪崩是指短时间内,有大量缓存同时过期,导致大量的请求直接查询数据库,从而对数据库造成了巨大的压力,严重情况下可能会导致数据库宕机的情况叫做缓存雪崩。
我们先来看下正常情况下和缓存雪崩时程序的执行流程图,正常情况下系统的执行流程如下图所示:
缓存雪崩的执行流程如下:
从以上图片我们可以发现,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 和 数据库 都被穿透。
缓存穿透的常见解决方案有以下几个:
1.缓存空对象: 对于查询结果为 nul 或不存在的数据,也可以将它们以特殊值(如"NULL"、特定标识符)进行缓存,并设置较短的过期时间。这样,短时间内相同的查询请求就可以直接从缓存中获得响应,避免了对数据库的直接查询。
2. 布隆过滤器(Bloom Filter): 在请求到达缓存之前,先通过布降过滤器判断数据可能存在还是一定不存在。对于确定不存在的数据,可以直接返回;可能存在则继续査询缓存和数据库。布隆过滤器是一种空间效率极高的概率型数据结构,它会给出“可能存在“或“肯定不存在”的答案。
3. 开启限流功能:当发现大量连接未命中的请求时,可以采用限流策略限制同一时间段内向数据库发送的查询请求数量,减轻数据库压力。
布隆过滤器是一种空间效率极高的概率性数据结构,可以用于判断一个元素是否在一个集合中。
它基于位数组和多个哈希函数的原理,可以高效地进行元素的查询,并且占用的空间相对较小,
如下图所示:
这里面存的就是比特,即0或1。根据 key 值计算出它的存储位置,然后将此位置标识全部标识为 1(未存放数据的位置全部为 0),查询时也是查询对应的位置是否全部为 1,如果全部为 1,则说明数据是可能存在的,否则一定不存在.
这种采用存储三个比特的方法,可以有效避免hash冲突,因为如果只存储一个的话,hash冲突可能发生比较频繁。
也就是说,如果布隆过滤器说一个元素不在集合中,那么它一定不在这个集合中;但如果它说一个元素在集合中则有可能是不存在的(存在误差)。
执行过程
布隆过滤器的具体执行步骤如下:
使用场景
布隆过滤器的主要使用场景有以下几个:
布隆过滤器会不会比较耗内存,添加数据怎么办,删除数据怎么办
布隆过滤器是一种用于快速判断一个元素是否存在于集合中的数据结构,它的优点是查询效率高且占用内存较少。然而,布隆过滤器也存在一些限制和操作上的考虑。
需要注意的是,布隆过滤器在判断元素是否存在时,可能存在一定的误判率。因此,在使用布隆过滤器时,需要根据具体的应用场景和需求来选择合适的误判率和内存消耗。
缓存击穿指的是某个热点缓存,在某一时刻恰好失效了,然后此时刚好有大量的并发请求,此时这些请求将会给数据库造成巨大的压力,这种情况就叫做缓存击穿。缓存击穿的执行流程如下图所示:
主要原因是: 热点数据再缓存中失效或淘汰,并发请求同时访问该数据库,导致缓存无法命中。
缓存击穿的执行流程:(Redis 被击穿)
① 设置永不过期:
对于某些热点缓存,我们可以设置永不过期,这样就能保证缓存的稳定性,但需要注意在数据更改之后,要及时更新此热点缓存,不然就会造成查询结果的误差。
② 缓存过期前预加载
在缓存即将过期之前,提前异步加载缓存,避免在缓存失效时大量请求直接打到数据库或者后端服务。
例如看门狗机制,但是实现起来并不简单,因为还需要设置定时任务之类,但是定时任务也有可能会挂,并且也是有一定开销。
③ 使用多级缓存:
可以使用多级缓存架构,将热门数据同时缓存在多个缓存节点上,避免单一节点故障导致请求直接访问数据库或者后端服务,例如可以设计多级缓存,也就是使用分布式缓存(Redis)+本地缓存(Caffeine/Guava Cache),如下图所示:
但是设计二级缓存需要多写很多代码,而且会增加系统的复杂性。虽然查询的时候,走二级缓存没有问题,但是应用程序执行写入操作的时候,那么原本只需要保证Redis里的数据库和数据库里的数据一致即可,但是现在还要保证二级缓存的一致性,数据的一致性更难保证了。
但是Caffeine有方案可以保证本地缓存一致性的问题。
④ 开启限流或降级功能:
当缓存发送雪崩时,采用限流或降级的机制来减少服务器的压力,保证系统的可用性。
如何保证本地缓存的一致性?
在分布式系统中,使用本地缓存最大的问题就是一致性问题,所谓的一致性问题指的是当数据库发生数据变更时缓存也要跟着一起变更。而分布式系统中每台机器都有自己的本地缓存,所以想要保证(本地缓存的)一致性是个比较难的问题,但通过以下手段可以最大程度的保证本地缓存的一致性问题:
不同的业务系统,会采用不同的解决方案,例如以下这些场景和对应的解决方案:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。