缓存预热!真香

2024-02-13 16:04
文章标签 缓存 真香 预热

本文主要是介绍缓存预热!真香,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

预热一般指缓存预热,一般用在高并发系统中,为了提升系统在高并发情况下的稳定性的一种手段。

缓存预热是指在系统启动之前或系统达到高峰期之前,通过预先将常用数据加载到缓存中,以提高缓存命中率和系统性能的过程。缓存预热的目的是尽可能地避免缓存击穿和缓存雪崩,还可以减轻后端存储系统的负载,提高系统的响应速度和吞吐量。
 

预热的必要性


缓存预热的好处有很多,如:

  1. 减少冷启动影响:当系统重启或新启动时,缓存是空的,这被称为冷启动。冷启动可能导致首次请求处理缓慢,因为数据需要从慢速存储(如数据库)检索。

  2. 提高数据访问速度:通过预先加载常用数据到缓存中,可以确保数据快速可用,从而加快数据访问速度。

  3. 平滑流量峰值:在流量高峰期之前预热缓存可以帮助系统更好地处理高流量,避免在流量激增时出现性能下降。

  4. 保证数据的时效性:定期预热可以保证缓存中的数据是最新的,特别是对于高度依赖于实时数据的系统。

  5. 减少对后端系统的压力:通过缓存预热,可以减少对数据库或其他后端服务的直接查询,从而减轻它们的负载。

预热的方法

缓存预热的一般做法是在系统启动或系统空闲期间,将常用的数据加载到缓存中,主要做法有以下几种:

系统启动时加载:在系统启动时,将常用的数据加载到缓存中,以便后续的访问可以直接从缓存中获取。

定时任务加载:定时执行任务,将常用的数据加载到缓存中,以保持缓存中数据的实时性和准确性。

手动触发加载:在系统达到高峰期之前,手动触发加载常用数据到缓存中,以提高缓存命中率和系统性能。

用时加载:在用户请求到来时,根据用户的访问模式和业务需求,动态地将数据加载到缓存中。

缓存加载器:一些缓存框架提供了缓存加载器的机制,可以在缓存中不存在数据时,自动调用加载器加载数据到缓存中。

Redis预热

在分布式缓存中,我们通常都是使用Redis,针对Redis的预热,有以下几个工具可供使用,帮助我们实现缓存的预热:

RedisBloom:RedisBloom是Redis的一个模块,提供了多个数据结构,包括布隆过滤器、计数器、和TopK数据结构等。其中,布隆过滤器可以用于Redis缓存预热,通过将预热数据添加到布隆过滤器中,可以快速判断一个键是否存在于缓存中。

Redis Bulk loading:这是一个官方出的,基于Redis协议批量写入数据的工具

Redis Desktop Manager:Redis Desktop Manager是一个图形化的Redis客户端,可以用于管理Redis数据库和进行缓存预热。通过Redis Desktop Manager,可以轻松地将预热数据批量导入到Redis缓存中。

应用启动时预热

ApplicationReadyEvent

在应用程序启动时,可以通过监听应用启动事件,或者在应用的初始化阶段,将需要缓存的数据加载到缓存中。

ApplicationReadyEvent 是 Spring Boot 框架中的一个事件类,它表示应用程序已经准备好接收请求,即应用程序已启动且上下文已刷新。这个事件是在 ApplicationContext 被初始化和刷新,并且应用程序已经准备好处理请求时触发的。

基于ApplicationReadyEvent,我们可以在应用程序完全启动并处于可用状态后执行一些初始化逻辑。使用 @EventListener 注解或实现 ApplicationListener 接口来监听这个事件。例如,使用 @EventListener 注解:

@EventListener(ApplicationReadyEvent.class)
public void preloadCache() {// 在应用启动后执行缓存预热逻辑// ...
}

Runner

如果你不想直接监听ApplicationReadyEvent,在SpringBoot中,也可以通过CommandLineRunner 和 ApplicationRunner 来实现这个功能。

CommandLineRunner 和 ApplicationRunner 是 Spring Boot 中用于在应用程序启动后执行特定逻辑的接口。这解释听上去就像是专门干这个事儿的。

import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;@Component
public class MyCommandLineRunner implements CommandLineRunner {@Overridepublic void run(String... args) throws Exception {// 在应用启动后执行缓存预热逻辑// ...}
}

import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;@Component
public class MyApplicationRunner implements ApplicationRunner {@Overridepublic void run(ApplicationArguments args) throws Exception {// 在应用启动后执行缓存预热逻辑// ...}

}

CommandLineRunner 和 ApplicationRunner的调用,是在SpringApplication的run方法中

其实就是callRunners(context, applicationArguments);的实现:

private void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList<>();runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());AnnotationAwareOrderComparator.sort(runners);for (Object runner : new LinkedHashSet<>(runners)) {if (runner instanceof ApplicationRunner) {callRunner((ApplicationRunner) runner, args);}if (runner instanceof CommandLineRunner) {callRunner((CommandLineRunner) runner, args);}}
}

使用InitializingBean接口

实现 InitializingBean 接口,并在 afterPropertiesSet 方法中执行缓存预热的逻辑。这样,Spring 在初始化 Bean 时会调用 afterPropertiesSet 方法。

import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;@Component
public class CachePreloader implements InitializingBean {@Overridepublic void afterPropertiesSet() throws Exception {// 执行缓存预热逻辑// ...}
}

这个方法的调用我们在Spring的启动流程中也介绍过,不再展开了

使用@PostConstruct注解

类似的,我们还可以使用 @PostConstruct 注解标注一个方法,该方法将在 Bean 的构造函数执行完毕后立即被调用。在这个方法中执行缓存预热的逻辑。

import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;@Component
public class CachePreloader {@PostConstructpublic void preloadCache() {// 执行缓存预热逻辑// ...}
}

定时任务预热

在启动过程中预热有一个问题,那就是一旦启动之后,如果需要预热新的数据,或者需要修改数据,就不支持了,那么,在应用的运行过程中,我们也是可以通过定时任务来实现缓存的更新预热的。

我们通常依赖这种方式来确保缓存中的数据是最新的,避免因为业务数据的变化而导致缓存数据过时。

在Spring中,想要实现一个定时任务也挺简单的,基于@Scheduled就可以轻易实现.

@Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行
public void scheduledCachePreload() {// 执行缓存预热逻辑// ...
}

也可以依赖xxl-job等定时任务实现。

缓存器预热

些缓存框架提供了缓存加载器的机制,可以在缓存中不存在数据时,自动调用加载器加载数据到缓存中。这样可以简化缓存预热的逻辑。如Caffeine中就有这样的功能:

import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import org.springframework.stereotype.Service;import java.util.concurrent.TimeUnit;@Service
public class MyCacheService {private final LoadingCache<String, String> cache;public MyCacheService() {this.cache = Caffeine.newBuilder().refreshAfterWrite(1, TimeUnit.MINUTES)  // 配置自动刷新,1分钟刷新一次.build(key -> loadDataFromSource(key));  // 使用加载器加载数据}public String getValue(String key) {return cache.get(key);}private String loadDataFromSource(String key) {// 从数据源加载数据的逻辑// 这里只是一个示例,实际应用中可能是从数据库、外部服务等获取数据System.out.println("Loading data for key: " + key);return "Value for " + key;}
}

在上面的例子中,我们使用 Caffeine.newBuilder().refreshAfterWrite(1, TimeUnit.MINUTES) 配置了缓存的自动刷新机制,即每个缓存项在写入后的1分钟内,如果有读请求,Caffeine 会自动触发数据的刷新。

loadDataFromSource 方法是用于加载数据的自定义方法。你可以在这个方法中实现从数据源(例如数据库、外部服务)加载数据的逻辑。

这篇关于缓存预热!真香的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/705972

相关文章

Linux修改pip和conda缓存路径的几种方法

《Linux修改pip和conda缓存路径的几种方法》在Python生态中,pip和conda是两种常见的软件包管理工具,它们在安装、更新和卸载软件包时都会使用缓存来提高效率,适当地修改它们的缓存路径... 目录一、pip 和 conda 的缓存机制1. pip 的缓存机制默认缓存路径2. conda 的缓

Redis解决缓存击穿问题的两种方法

《Redis解决缓存击穿问题的两种方法》缓存击穿问题也叫热点Key问题,就是⼀个被高并发访问并且缓存重建业务较复杂的key突然失效了,无数的请求访问会在瞬间给数据库带来巨大的冲击,本文给大家介绍了Re... 目录引言解决办法互斥锁(强一致,性能差)逻辑过期(高可用,性能优)设计逻辑过期时间引言缓存击穿:给

如何通过Golang的container/list实现LRU缓存算法

《如何通过Golang的container/list实现LRU缓存算法》文章介绍了Go语言中container/list包实现的双向链表,并探讨了如何使用链表实现LRU缓存,LRU缓存通过维护一个双向... 目录力扣:146. LRU 缓存主要结构 List 和 Element常用方法1. 初始化链表2.

一文详解Nginx的强缓存和协商缓存

《一文详解Nginx的强缓存和协商缓存》这篇文章主要为大家详细介绍了Nginx中强缓存和协商缓存的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录一、强缓存(Strong Cache)1. 定义2. 响应头3. Nginx 配置示例4. 行为5. 适用场景二、协商缓存(协

Golang基于内存的键值存储缓存库go-cache

《Golang基于内存的键值存储缓存库go-cache》go-cache是一个内存中的key:valuestore/cache库,适用于单机应用程序,本文主要介绍了Golang基于内存的键值存储缓存库... 目录文档安装方法示例1示例2使用注意点优点缺点go-cache 和 Redis 缓存对比1)功能特性

MySQL8.0设置redo缓存大小的实现

《MySQL8.0设置redo缓存大小的实现》本文主要在MySQL8.0.30及之后版本中使用innodb_redo_log_capacity参数在线更改redo缓存文件大小,下面就来介绍一下,具有一... mysql 8.0.30及之后版本可以使用innodb_redo_log_capacity参数来更改

MySQL 缓存机制与架构解析(最新推荐)

《MySQL缓存机制与架构解析(最新推荐)》本文详细介绍了MySQL的缓存机制和整体架构,包括一级缓存(InnoDBBufferPool)和二级缓存(QueryCache),文章还探讨了SQL... 目录一、mysql缓存机制概述二、MySQL整体架构三、SQL查询执行全流程四、MySQL 8.0为何移除查

Redis缓存问题与缓存更新机制详解

《Redis缓存问题与缓存更新机制详解》本文主要介绍了缓存问题及其解决方案,包括缓存穿透、缓存击穿、缓存雪崩等问题的成因以及相应的预防和解决方法,同时,还详细探讨了缓存更新机制,包括不同情况下的缓存更... 目录一、缓存问题1.1 缓存穿透1.1.1 问题来源1.1.2 解决方案1.2 缓存击穿1.2.1

Redis与缓存解读

《Redis与缓存解读》文章介绍了Redis作为缓存层的优势和缺点,并分析了六种缓存更新策略,包括超时剔除、先删缓存再更新数据库、旁路缓存、先更新数据库再删缓存、先更新数据库再更新缓存、读写穿透和异步... 目录缓存缓存优缺点缓存更新策略超时剔除先删缓存再更新数据库旁路缓存(先更新数据库,再删缓存)先更新数

el-select下拉选择缓存的实现

《el-select下拉选择缓存的实现》本文主要介绍了在使用el-select实现下拉选择缓存时遇到的问题及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录项目场景:问题描述解决方案:项目场景:从左侧列表中选取字段填入右侧下拉多选框,用户可以对右侧