缓存雪崩+穿透+击穿+预热只看这一篇就够了超级详细

什么是缓存雪崩?如何解决缓存雪崩?

缓存雪崩指的是在短时间内,有大量的请求直接查询术后句酷,从而对数据库造成大量的压力,严重情况下可能导致数据库宕机的情况叫做缓存雪崩

我们可以看一下正常的情况下程序执行流程图:

 当出现缓存雪崩的时候,流程图如下:

由此可以看出缓存雪崩造成的影响,导致缓存雪崩的主要原因有以下几种:

  1. 缓存过期时间设置不合理:当大量缓存数据在同一时间失效时,会导致大量请求直接打到数据库或者后端服务
  2. 缓存服务器故障:如果缓存服务器发生故障,无法体哦共缓存服务,那么所有请求都会直接访问数据库或后端服务
  3. 缓存数据的热点分布不均:如果某些热门数据集中一部分缓存节点上,当这些节点发生故障或者数据失效的 ,会导致请求直接打到数据库或者后端服务

如何解决缓存雪崩

缓存雪崩的常见解决方法有以下几种:

  • 随机生成缓存过期时间:随机生成缓存过期时间,可以避免缓存同时过期,从而让避免缓存雪崩问题
//缓存原本的失效时间
int exTime = 10*60;
//随机数生成类
Random random = new Random();
//缓存设置
jedis.setex(cacheKey,exTime+random.nextInt(1000) ,value);
  • 使用多级缓存:可以使用多级缓存架构,将热门数据同时缓存在多个缓存节点上,避免单一节点故障导致请求直接访问数据库或者后端服务,例如可以设计二级换内存(分布式缓存+本地缓存),如图:

  • 缓存过期前预加载:在缓存即将过期之前,提前异步加载缓存,避免在缓存失效时大量请求直接打到数据库或者后端服务
  • 开启限流或降级功能:当缓存发生雪崩时,采用限流或降级的机制来减轻服务器压力,保证系统可用性
  • 实时及监控和预警:通过监控缓存的状态和命中率,及时发现缓存的问题

什么是缓存穿透?如何解决缓存穿透?

缓存穿透是指,当我们查询一个数据库和缓存中都不存在的数据时,由于数据库查询结果为空,出于容错考虑,我们通常不会将这个空结果保存到缓存中。因此,每次对这个数据的请求都会直接查询数据库,而不是缓存。这就导致数据库需要处理额外的查询压力,从而可能降低系统的整体性能

简单来说,缓存穿透就是指数据库查询没有数据,出于容错考虑,不会将结果保存到缓存中,因此每次请求都会去查询数据库

缓存穿透执行流程如下:

其中红色路径代表缓存穿透的执行路径,可以看出缓存穿透会给数据库造成很大压力

如何解决缓存穿透

  1. 缓存空对象:对于查询结果为null或不存在的数据,也可以将它们以特殊值(如:NULL或特殊符号)进行缓存,并设置较短的过期时间。这样,短时间相同的查询请求就可以直接从缓存中获得响应,避免了对数据库的直接查询
  2. 布隆过滤器:在请求达到缓存之前,先通过布隆过滤器判断数据可能存在还是一定不存在。对于不存在的数据,可以直接返回;可能存在的则继续查询缓存和数据库。布隆过滤器是一种空间效率极高的概率型数据结构,他会给出“可能存在”或者“一定不存在”的答案
  3. 开启限流功能:当发现大量连续未命中的请求的时候,可以采用限流策略限制同一时间内向数据库发送的查询请求数量,减轻数据库压力

什么是缓存击穿?如何解决缓存击穿?

缓存击穿是指某个热点缓存,在某一时刻恰好失效了,然后此时刚好有大量的并发请求,此时这些请求会给数据库造成巨大的压力

缓存击穿的执行流程:

缓存击穿主要的原因是热点数据在缓存中失效或被淘汰,并发请求同属访问该数据,导致缓存无法命中

如何解决缓存击穿 

  • 设置永不过期:对于某些热点缓存,我们可以设置成永不过期,这样就保证缓存的稳定性,但是需要注意在数据更改之后,要及时更新此热点缓存,不然会造成查询结果的误差
  • 缓存过期前预加载:在缓存即将过期之前,提前异步加载缓存,避免在缓存失效时大量的请求直接打到数据库或者后端服务
  • 使用多级缓存:可以使用多级缓存架构,将热门数据同时缓存在多个缓存节点上,避免单一节点故障导致请求直接访问数据库或者后端服务。例如可以设计多级缓存,也就是使用分布式缓存(Redis)+本地缓存(Caffeine/Guava Cache) ,如下图所示:

  • 开启限流或降级功能:当缓存发生雪崩时,采用限流或降级的机制来减轻服务器压力,保证系统可用性

什么是缓存预热?如何实现缓存预热?

缓存与炽热是指在系统启动、高峰期来临之前或者数据变更之后,提前将热门或者需要经常访问的数据加载到缓存中,以提高系统的响应性能和缓存命中率。通过缓存预热,可以避免在实际请求到来的时候出现缓存穿透和缓存击穿的情况,减少对后端存储的直接访问

实现缓存你预热的一般步骤如下:

  1. 确定热门数据:首先需要确定哪些数据是热门或者经常访问的数据。可以通过系统日志、业务需求、数据统计分析等方式进行评估
  2. 加载数据到缓存:在系统启动、高峰期来临之前或者数据变更之后,提前将热门数据加载到缓存中。可以通过定时任务、异步加载、批量加载等方式来实现数据加载
  3. 设置适当的过期时间:根据业务需求和数据的访问频率,设置适当的缓存过期时间。过期时间可以根据不同的数据进行灵活调整,以保证缓存数据的有效性
  4. 监控和维护:在缓存预热完成后,需要进行监控和维护。可以通过监控缓存命中率、缓存失效率指标来评估

缓存预热的实现

手动初始化:在程序启动阶段或者服务初始化的时候,通过编写代码主动的从数据库加载热点数据,并将其放入缓存中(如:Redis)

//初始化阶段加载热点数据
public void warmUpCache() {
    List<HotData> hotDatas = loadHotDataFromDatabase();
    for (HotData data : hotDatas) {
        string key = buildKey(data.getId());
        redisTemplate.opsForValue().set(key, data expirationTime TimeUnit.MINUTES);
    }
}

定时任务:使用定时任务定期刷新或者加载数据到缓存中,可以是固定时间间隔,也可以是在数变更后触发

事件驱动:当有新的数据添加到数据库时,触发一个时间来通知缓存系统加载数据

使用框架:某些框架或者中间件提供了缓存预热功能的支持。例如,在Spring Boot项目中,可以通过实现CommandLineRunner或ApplicationRunner接口,在应用启动自动加载数据到缓存

你的代码片段似乎有一些语法错误,我已经为你修正了。在Spring Boot中,一个类可以同时实现CommandLineRunnerApplicationRunner接口,并且可以在run方法中添加自定义的操作。以下是修正后的代码:

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class MyRunner implements CommandLineRunner, ApplicationRunner {
    @Override
    public void run(String... args) throws Exception {
        System.out.println("This is CommandLineRunner"); // 实现自定义操作
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        System.out.println("This is ApplicationRunner"); // 实现自定义操作
    }
}

在这段代码中,MyRunner类实现了CommandLineRunnerApplicationRunner接口。当Spring Boot应用启动完成后,它会自动执行这两个接口的run方法。在这两个方法中,你可以添加自定义的操作,比如加载数据到缓存(即缓存预热)等

  • 47
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lose_rose777

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值