缓存预热有哪些方案?
一道经典面试题:缓存预热有哪些方案?
在系统业务高峰期到来之前,我们提前将一些热点数据加载到缓存中,进而提高系统的响应速度,这就是所谓的缓存预热。
那么怎么实现缓存预热呢?
一般来说,我们主要有三种思路:
- 系统启动时加载缓存。
- 定时任务加载缓存。
- 使用缓存加载器。
每种里边往往又对应了不同的具体方案,我们逐一来看。
一 系统启动时加载缓存
这个就是利用系统启动时候的一些钩子函数,或者如事件监听机制或者是框架专为系统启动预留的方法,在这些方法中去加载缓存。
这里松哥给四个常见的思路。
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> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {// 假设有一个缓存管理器cacheManagercacheManager.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;}@PostConstructpublic 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;}@Overridepublic 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;}@Overridepublic 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;}@Overridepublic 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>() {@Overridepublic 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;}@Overridepublic 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 了,系统会自动执行。
好啦,缓存预热的三种思路,小伙伴们平时都是怎么做的?欢迎留言讨论。
相关文章:

缓存预热有哪些方案?
一道经典面试题:缓存预热有哪些方案? 在系统业务高峰期到来之前,我们提前将一些热点数据加载到缓存中,进而提高系统的响应速度,这就是所谓的缓存预热。 那么怎么实现缓存预热呢? 一般来说,我…...

「iOS学习」——Masonry学习
iOS学习 前言Masonry的属性Masonry的使用基础APIAuto Boxing修饰语倍数中心点设置边距优先级使用 总结 前言 暑假我们学习了使用CocoaPods引入第三方库,实现使用SVG图片。而Masonry作为一个轻量级的布局架构,在使用中可以节省很多时间。故进行简单学习。…...

828华为云征文|华为云Flexus云服务器X实例之openEuler系统下部署GitLab服务器
828华为云征文|华为云Flexus云服务器X实例之openEuler系统下部署Gitlab服务器 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、GitLab介绍2.1 GitLab简介2.2 GitLab主要特点 三、本次…...

51单片机的无线病床呼叫系统【proteus仿真+程序+报告+原理图+演示视频】
1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块温湿度传感器模块矩阵按键时钟模块等模块构成。适用于病床呼叫系统、16床位呼叫等相似项目。 可实现基本功能: 1、LCD1602实时显示北京时间、温湿度信息、呼叫床位等信息; 2、DHT11采集病房温湿度信息&…...

计算机毕业设计 | SpringBoot+vue 游戏商城 steam网站管理系统(附源码)
1,项目背景 国家大力推进信息化建设的大背景下,城市网络基础设施和信息化应用水平得到了极大的提高和提高。特别是在经济发达的沿海地区,商业和服务业也比较发达,公众接受新事物的能力和消费水平也比较高。开展商贸流通产业的信息…...

【CH395的简单示例代码】
提供一个基于CH395的简单示例代码,这里将展示如何初始化CH395,并发送一个简单的HTTP请求。请注意,实际使用时还需要根据具体的硬件平台和开发环境调整代码。 假设我们使用的是一个具有SPI接口的微控制器,并且已经将CH395连接到该…...

AI模型:追求全能还是专精?
目录 引言 一、全能型AI模型的诱惑 1.1 通用智能的愿景 1.2 资源整合的优势 1.3 应对未知挑战的能力 1.4 挑战与不足 二、专精型AI模型的魅力 2.1 深度与精度的提升 2.2 成本控制与效率优化 2.3 易于监管与解释性增强 2.4 挑战与不足 三、全能型与专精型AI的全面评…...

ffmpeg音视频开发从入门到精通——ffmpeg 视频数据抽取
文章目录 FFmpeg视频处理工具使用总结环境配置主函数与参数处理打开输入文件获取流信息分配输出文件上下文猜测输出文件格式创建视频流并设置参数打开输出文件并写入头信息读取、转换并写入帧数据写入尾信息并释放资源运行程序注意事项源代码 FFmpeg视频处理工具使用总结 环境…...

Node.js之文件夹的操作
1.创建文件夹操作 // 导入fs模块 const fs require(fs)//创建文件夹操操作 fs.mkdir(./Page, err > {if (err) {console.log(操作失败)return}console.log(操作成功) }) 2.递归创建文件夹 //递归创建文件夹 fs.mkdir(./a/b/c, {recursive: true}, err > {if (err) {co…...

线程的四种操作
所属专栏:Java学习 1. 线程的开启 start和run的区别: run:描述了线程要执行的任务,也可以称为线程的入口 start:调用系统函数,真正的在系统内核中创建线程(创建PCB,加入到链…...

自我指导:提升语言模型自我生成指令的能力
人工智能咨询培训老师叶梓 转载标明出处 传统的语言模型,尤其是经过指令微调的大型模型,虽然在零样本(zero-shot)任务泛化上表现出色,但它们高度依赖于人类编写的指令数据。这些数据往往数量有限、多样性不足…...

使用Node.js实现单文件上传功能—含代码解释
1、概念 文件上传的具体内容 在前端让用户发送(上传)图片,图片由后端(服务器)接收,并转存到到服务端设备上的操作node.js的文件上传功能主要是使用:multer 插件实现的 搭建一个图片上传的接口 先让接口开通,再去做插件下载/配置等…...

【机器人工具箱Robotics Toolbox开发笔记(一)】Matlab机器人工具箱简介
MATLAB是一款被广泛应用于科学计算和工程领域的专业软件。它的全称为Matrix Laboratory(矩阵实验室),因为其最基本的数据类型就是矢量与矩阵,所以在处理数学和科学问题时非常方便,可用于线性代数计算、图形和动态仿真的…...

基于 Metropolis 的朗之万算法
基于 Metropolis 的朗之万算法 1. 未经调整的朗之万算法2. 基于 Metropolis 的朗之万算法 (MALA)2.1. MH算法2.2. 基于 Metropolis 的朗之万算法 (MALA) 3. Metropolis 调整的朗之万截断算法(MALTA) 1. 未经调整的朗之万算法 未调整的朗之万算法 (ULA) 是…...

SAM2POINT:以zero-shot且快速的方式将任何 3D 视频分割为视频
摘要 我们介绍 SAM2POINT,这是一种采用 Segment Anything Model 2 (SAM 2) 进行零样本和快速 3D 分割的初步探索。 SAM2POINT 将任何 3D 数据解释为一系列多向视频,并利用 SAM 2 进行 3D 空间分割,无需进一步训练或 2D-3D 投影。 我们的框架…...

深入理解FastAPI的response_model:自动化数据验证与文档生成
使用 FastAPI 的 response_model 参数 在构建 RESTful API 时,确保数据的一致性和正确性是非常重要的。FastAPI 提供了强大的工具来帮助开发者实现这一目标。其中一个关键特性是 response_model 参数,它允许开发者定义期望的响应格式,并自动…...

【数据结构与算法 | 灵神题单 | 删除链表篇】力扣3217, 82, 237
总结,删除链表节点问题使用到列表,哈希表,递归比较容易超时,我觉得使用计数排序比较稳,处理起来也不是很难。 1. 力扣3217:从链表中移除在数组中的节点 1.1 题目: 给你一个整数数组 nums 和一…...

快速失败 (fail-fast) 和安全失败 (fail-safe)
1. 定义与工作原理 1.1 快速失败(Fail-Fast) 定义: 快速失败是一种系统设计原则,当系统遇到异常情况或错误时,立即停止执行并返回错误,而不是试图继续执行或处理潜在的问题。快速失败系统会主动检测系统中…...

【MySQL】MySQL中表的增删改查——(基础篇)(超详解)
前言: 🌟🌟本期讲解关于MySQL中CDUD的基础操作,希望能帮到屏幕前的你。 🌈上期博客在这里:http://t.csdnimg.cn/fNldO 🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客 目录 …...

【B题第二套完整论文已出】2024数模国赛B题第二套完整论文+可运行代码参考(无偿分享)
2024数模国赛B题完整论文 摘要: 随着电子产品制造业的快速发展,质量控制与成本优化问题成为生产过程中亟待解决的核心挑战。为应对生产环节中的质量不确定性及成本控制需求,本文结合抽样检测理论和成本效益分析,通过构建数学模型…...

大数据之Flink(四)
11、水位线 11.1、水位线概念 一般实时流处理场景中,事件时间基本与处理时间保持同步,可能会略微延迟。 flink中用来衡量事件时间进展的标记就是水位线(WaterMark)。水位线可以看作一条特殊的数据记录,它是插入到数…...

《Web性能权威指南》-网络技术概览-读书笔记
注:TCP/IP等知识牵涉面太广,且不说本文,哪怕是原书,限于篇幅,很多知识点都是大致介绍下。如果想深入理解,需要更一步Google相关页面资料。 延迟与带宽 WPO,Web Performance Optimization&…...

最新版php进销存系统源码 ERP进销存专业化管理 永久免费升级更新+完整图文搭建教程
在当今信息化时代,企业管理的高效性与精确性是企业竞争力的关键。分享一款最新版的PHP进销存系统源码,一款专为企业设计的ERP进销存管理工具,其丰富的功能、灵活的子账号设置、强大的权限控制、以及独家升级的合同管理和报价单打印功能&#…...

【高效办公】三、两台电脑共享鼠标、键盘和文件,两台电脑当一个用的神操作!barrier
1.下载 ubuntu:sudo apt install barrierwindows:https://github.com/debauchee/barrier/releases-下载 : 2.4.0-Assets-BarrierSetup-2.4.0-release.exe 2.运行 ubuntu:sudo apt install barrierwindows:https://github.com/debauchee/barrier/releases-下载 : 2.4.0-Asset…...

智能合约系统DAPP开发
智能合约系统DAPP(去中心化应用)的开发是一个复杂且综合性的过程,它结合了区块链技术、智能合约编程、前端开发以及安全性等多方面的知识和技能。以下是对智能合约系统DAPP开发过程的详细概述: 一、需求分析 明确应用场景…...

宠物狗检测-目标检测数据集(包括VOC格式、YOLO格式)
宠物狗检测-目标检测数据集(包括VOC格式、YOLO格式) 数据集: 链接:https://pan.baidu.com/s/1roegkaGAURWUVRR-D7OzzA?pwddxv6 提取码:dxv6 数据集信息介绍: 共有20580 张图像和一一对应的标注文件 标…...

2.5多任务示例编程2
1.CUBEMX配置 2.代码 void StartADC(void const * argument) {/* USER CODE BEGIN StartADC */TickType_t pxPreviousWakeTimexTaskGetTickCount();/* Infinite loop */for(;;){HAL_ADC_Start(&hadc1);if(HAL_ADC_PollForConversion(&hadc1,100)HAL_OK){uint32_t valu…...

JavaWeb - 4 - Vue Ajax
一.Vue Vue Vue是一套前端框架,免除原生JavaScript中的DOM操作,简化书写 基于MVVM(Model-VIew-ViewModel)思想,实现数据的双向绑定,将编程的关注点放在数据上 官网:https://cn.vuejs.org…...

深入掌握Go语言中的正则表达式与字符串处理
Go语言中的正则表达式与模式匹配 在编程中,字符串处理是常见的需求之一,而正则表达式则是一个强大的工具,能够帮助我们实现复杂的字符串匹配、提取和替换功能。Go语言内置了对正则表达式的支持,通过regexp包,我们可以…...

Docker进入容器运行命令
Docker进入容器运行命令 1. **使用 docker exec 进入容器并运行命令**语法:示例 1:进入容器并启动交互式 Bash 终端示例 2:在容器中运行单个命令 2. **使用 docker attach 进入容器**3. **使用 docker run 启动新容器并运行命令**4. **使用 d…...