缓存预热有哪些方案?
一道经典面试题:缓存预热有哪些方案?
在系统业务高峰期到来之前,我们提前将一些热点数据加载到缓存中,进而提高系统的响应速度,这就是所谓的缓存预热。
那么怎么实现缓存预热呢?
一般来说,我们主要有三种思路:
- 系统启动时加载缓存。
- 定时任务加载缓存。
- 使用缓存加载器。
每种里边往往又对应了不同的具体方案,我们逐一来看。
一 系统启动时加载缓存
这个就是利用系统启动时候的一些钩子函数,或者如事件监听机制或者是框架专为系统启动预留的方法,在这些方法中去加载缓存。
这里松哥给四个常见的思路。
1.1 启动监听事件
使用 ApplicationListener
监听 Spring Boot 启动完成的事件,如 ContextRefreshedEvent
或 ApplicationReadyEvent
,在事件触发后加载数据到缓存。
举个栗子:
import org.springframework.context.ApplicationListener; import org.springframework.context.event.ContextRefreshedEvent; import org.springframework.stereotype.Component; @Component public class CacheWarmer implements ApplicationListener<contextrefreshedevent> { @Override public void onApplicationEvent(ContextRefreshedEvent event) { // 假设有一个缓存管理器cacheManager cacheManager.put("key1", "松哥"); cacheManager.put("key2", "江南一点雨"); // ... 加载更多数据到缓存 } }
1.2 @PostConstruct 注解
使用 @PostConstruct
注解在 Spring Bean 初始化后立即执行缓存预热逻辑。
举个栗子:
import javax.annotation.PostConstruct; import org.springframework.stereotype.Component; @Component public class CachePreloader { private final CacheManager cacheManager; public CachePreloader(CacheManager cacheManager) { this.cacheManager = cacheManager; } @PostConstruct public void preloadCache() { cacheManager.put("key1", "江南一点雨"); cacheManager.put("key2", "松哥"); // ... 加载更多数据到缓存 } }
1.3 CommandLineRunner 或 ApplicationRunner
实现 CommandLineRunner
或 ApplicationRunner
接口,在 Spring Boot 启动后执行缓存预热。
CommandLineRunner 案例:
import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class MyCommandLineRunner implements CommandLineRunner { private final CacheManager cacheManager; public MyCommandLineRunner(CacheManager cacheManager) { this.cacheManager = cacheManager; } @Override public void run(String... args) throws Exception { cacheManager.put("key1", "江南一点雨"); cacheManager.put("key2", "松哥"); // ... 加载更多数据到缓存 } }
ApplicationRunner 案例:
import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.stereotype.Component; @Component public class MyApplicationRunner implements ApplicationRunner { private final CacheManager cacheManager; public MyApplicationRunner(CacheManager cacheManager) { this.cacheManager = cacheManager; } @Override public void run(ApplicationArguments args) throws Exception { cacheManager.put("key1", "江南一点雨"); cacheManager.put("key2", "松哥"); // ... 加载更多数据到缓存 } }
1.4 InitializingBean接口
实现 InitializingBean
接口,并在 afterPropertiesSet
方法中执行缓存预热。
案例:
import org.springframework.beans.factory.InitializingBean; import org.springframework.stereotype.Component; @Component public class CachePreloader implements InitializingBean { private final CacheManager cacheManager; public CachePreloader(CacheManager cacheManager) { this.cacheManager = cacheManager; } @Override public void afterPropertiesSet() throws Exception { cacheManager.put("key1", "松哥"); cacheManager.put("key2", "江南一点雨"); // ... 加载更多数据到缓存 } }
在这些案例中,CacheManager
是一个假设的缓存管理器,你需要根据实际使用的缓存技术(如 Redis、EhCache 等)来实现或注入相应的缓存管理器。这些代码片段展示了如何在 Spring Boot 应用启动后,通过不同的方式加载数据到缓存中,以减少应用启动后的首次加载延迟。
在系统启动时加载缓存往往有一个问题,就是这些缓存加载之后,不会主动更新。如果我们需要对缓存进行定期更新,那么就可以考虑使用定时任务去加载缓存。
二 定时任务加载缓存
一般来说,如果我们面对以下需求时,就可以考虑使用定时任务加载缓存。
- 数据依赖性:当缓存的数据依赖于外部系统或数据库的定期更新,且这些更新不是实时触发的,而是按照一定的时间间隔发生时,可以通过定时任务来预热缓存。
- 数据量大:如果预热的数据量非常大,一次性加载可能会对系统性能产生影响,可以通过定时任务分批次逐步加载数据到缓存中。
- 依赖多个数据源:当缓存的数据需要从多个数据源聚合时,可以通过定时任务来协调这些数据源的更新,确保缓存的数据一致性和准确性。
- 业务逻辑复杂:如果缓存预热的业务逻辑比较复杂,涉及到多步骤处理或者需要等待某些异步操作完成,定时任务可以在特定时间点触发这些复杂的业务逻辑。
- 资源分配:在资源受限的环境中,为了避免在应用启动时占用过多资源,可以通过定时任务在系统资源较为空闲的时候进行缓存预热。
举个栗子:假设有一个电商网站,商品信息每天凌晨由供应商更新,并且更新操作不是实时的。在这种情况下,可以设置一个定时任务,在每天凌晨更新完成后,将最新的商品信息加载到缓存中,以便用户在白天访问时能够快速获取到最新的商品数据。
在 Spring 框架中,我们可以使用 @Scheduled
注解来实现定时任务:
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class CacheWarmUpTask { private final CacheManager cacheManager; public CacheWarmUpTask(CacheManager cacheManager) { this.cacheManager = cacheManager; } @Scheduled(cron = "0 0 1 * * ?") // 每天凌晨1点执行 public void warmUpCache() { // 执行缓存预热逻辑 cacheManager.put("key1", "江南一点雨"); cacheManager.put("key2", "松哥"); // ... 加载更多数据到缓存 } }
@Scheduled
注解用于指定定时任务的执行计划,cron
表达式定义了任务的执行时间。这样,每天凌晨 1 点,定时任务就会执行,将数据加载到缓存中。
三 使用缓存加载器
缓存加载器(Cache Loader)用于在缓存中自动加载数据。
当缓存中缺少请求的数据时,缓存加载器会被触发,以便从原始数据源(如数据库、文件系统或其他服务)中加载数据并将其放入缓存中。
缓存加载器的主要目的是减少直接从原始数据源加载数据的延迟,提高数据访问的速度。
缓存加载器通常与缓存库或框架一起使用,如 Guava Cache、EhCache、Caffeine 等。在 Spring Cache abstract 中,也可以通过自定义的缓存管理器来实现缓存加载器的功能。
3.1 使用缓存加载器步骤
- 定义缓存加载器逻辑:首先,我们需要定义一个加载数据的逻辑,这个逻辑会在缓存中缺失数据时被调用。
- 配置缓存:在缓存配置中指定缓存加载器。
- 预加载数据:在应用启动时或在特定时间点,通过调用缓存的获取方法来触发缓存加载器,从而预先加载数据到缓存中。
- 定时任务:如果需要定期更新缓存,可以结合定时任务(如 Spring 的
@Scheduled
注解)来定期触发缓存加载器。
3.2 举个栗子
Caffeine 是一个高性能的 Java 缓存库,它提供了丰富的缓存策略和灵活的配置选项。Caffeine 并没有直接的缓存预热 API,但是可以通过在应用启动时预先加载数据到缓存中来实现缓存预热的效果。
下面是使用 Caffeine 进行缓存预热的步骤:
- 定义缓存:首先,你需要定义一个 Caffeine 缓存实例,并配置相应的缓存策略。
- 预加载数据:在应用启动时,通过显式调用缓存的
get
方法或使用getAll
方法来加载数据到缓存中。 - 监听器:如果需要在缓存加载时执行额外的操作,可以配置
CacheLoader
的监听器。 - 异步加载:如果数据加载是异步的,可以使用
AsyncLoadingCache
接口来处理异步加载的情况。
代码案例
定义缓存:
import com.github.benmanes.caffeine.cache.CacheLoader; import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import java.util.concurrent.TimeUnit; public class CacheInitializer { public static LoadingCache<string, string> createCache() { CacheLoader<string, string> loader = new CacheLoader<string, string>() { @Override public String load(String key) { // 模拟从数据库或其他数据源加载数据 return "Value for " + key; } }; return Caffeine.newBuilder() .expireAfterWrite(10, TimeUnit.MINUTES) // 设置写入后过期时间 .maximumSize(1000) // 设置最大缓存项数 .build(loader); } }
预加载数据:
import com.github.benmanes.caffeine.cache.LoadingCache; import java.util.Arrays; import java.util.concurrent.ExecutionException; public class CacheWarmUp { private final LoadingCache<string, string> cache; public CacheWarmUp(LoadingCache<string, string> cache) { this.cache = cache; } public void warmUp() throws ExecutionException { // 预加载数据到缓存 Arrays.asList("key1", "key2", "key3").forEach(key -> { try { cache.get(key); // 这将触发加载器加载数据 } catch (ExecutionException e) { e.printStackTrace(); } }); } }
在 Spring Boot 应用中使用:
import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class CacheWarmUpRunner implements CommandLineRunner { private final LoadingCache<string, string> cache; public CacheWarmUpRunner(LoadingCache<string, string> cache) { this.cache = cache; } @Override public void run(String... args) { try { new CacheWarmUp(cache).warmUp(); } catch (Exception e) { e.printStackTrace(); } } }
在这个示例中,CacheInitializer
类定义了一个 Caffeine 缓存实例,CacheWarmUp
类包含了预加载数据到缓存的逻辑。CacheWarmUpRunner
是一个 Spring Boot 的 CommandLineRunner
,它在应用启动时调用 CacheWarmUp
类来预加载数据。
通过这种方式,你可以在应用启动时预先加载数据到 Caffeine 缓存中,从而实现缓存预热。这对于提高应用的响应速度和减少首次加载延迟非常有帮助。
上面的案例本质上还是利用了 CommandLineRunner,不过,LoadingCache 在配置的时候,其实也可以设置一个自动刷新的时间,这样就不需要 CommandLineRunner 了,系统会自动执行。
好啦,缓存预热的三种思路,小伙伴们平时都是怎么做的?欢迎留言讨论。</string,></string,></string,></string,></string,></string,></string,></contextrefreshedevent>

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
西游再现,函数计算一键部署 Flux 超写实文生图模型部署
从花果山的灵石出世,到取经路上的九九八十一难,再到大闹天宫的惊心动魄......这些耳熟能详的西游场景,如今都能通过Flux模型,以超乎想象的细节和真实感呈现在你眼前。**本次实验在函数计算中内置的flux.1-dev-fp8大模型,搭配Lora模型,**无需复杂的配置,一键部署,你就能成为这场视觉盛宴的创造者。 诚邀您参与到这场奇妙旅程中来,这不仅是一次技术的探索,更是一场创意的狂欢,在函数计算的助力下,书写属于自己的西游传奇! 立即参与 :https://developer.aliyun.com/topic/fc/august 实验准备 场景介绍 Flux 模型是由 Black Forest Labs (Stable Diffusion原班人马创立)推出的一款文本生成图像的AI模型,该模型以其高清晰度图像生成和优秀的提示词遵循能力而受到关注。Flux.1模型在图像质量、提示词遵循、尺寸和比例的多样性、排版能力以及输出的多样性等方面超越了市场上的主流文生图模型,在文字生成、非真实场景+文字生成、现实世界真实场景、真实人物、动漫人物以及动物生成上表现都非常出色。 领取免费额度 开通函数...
- 下一篇
TimeWheel 算法介绍及在应用上的探索
作者:来自 vivo 互联网服务器团队- Li Fan 本文从追溯时间轮算法的出现,介绍了时间轮算法未出现前,基于队列的定时任务实现,以及基于队列的定时任务实现所存在的缺陷。接着我们介绍了时间轮算法的算法思想及其数据结构,详细阐述了三种时间轮模型的数据结构和优劣性。 再次,我们介绍时间轮算法在 Dubbo 框架中的应用,并给出了它在 Dubbo 中的主要实现方式。 最后,我们以项目中的某个服务架构优化出发,介绍了目前设计中存在的缺陷,并借助来自中间件团队的,包含时间轮算法实现的延迟 MQ,给出了优化设计的方法。 第一章 定时任务及时间轮算法发展 1.1 时间轮算法的出现 在计算程序中,定时器用于指定一个具体的时间点去执行某一个既定的任务。而时间轮算法就是这样一种能够实现延迟功能(定时器)的巧妙算法。时间轮算法首次出现在1997年 George Varghese 和 Anthony Lauck 发表于IEEE期刊,名为“Hashed and Hierarchical Timing Wheels: Efficient Data Structures for Implementing a ...
相关文章
文章评论
共有0条评论来说两句吧...