缓存预热有哪些方案?
一道经典面试题:缓存预热有哪些方案?
在系统业务高峰期到来之前,我们提前将一些热点数据加载到缓存中,进而提高系统的响应速度,这就是所谓的缓存预热。
那么怎么实现缓存预热呢?
一般来说,我们主要有三种思路:
- 系统启动时加载缓存。
- 定时任务加载缓存。
- 使用缓存加载器。
每种里边往往又对应了不同的具体方案,我们逐一来看。
一 系统启动时加载缓存
这个就是利用系统启动时候的一些钩子函数,或者如事件监听机制或者是框架专为系统启动预留的方法,在这些方法中去加载缓存。
这里松哥给四个常见的思路。
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题完整论文 摘要: 随着电子产品制造业的快速发展,质量控制与成本优化问题成为生产过程中亟待解决的核心挑战。为应对生产环节中的质量不确定性及成本控制需求,本文结合抽样检测理论和成本效益分析,通过构建数学模型…...

VB.net复制Ntag213卡写入UID
本示例使用的发卡器:https://item.taobao.com/item.htm?ftt&id615391857885 一、读取旧Ntag卡的UID和数据 Private Sub Button15_Click(sender As Object, e As EventArgs) Handles Button15.Click轻松读卡技术支持:网站:Dim i, j As IntegerDim cardidhex, …...

【人工智能】神经网络的优化器optimizer(二):Adagrad自适应学习率优化器
一.自适应梯度算法Adagrad概述 Adagrad(Adaptive Gradient Algorithm)是一种自适应学习率的优化算法,由Duchi等人在2011年提出。其核心思想是针对不同参数自动调整学习率,适合处理稀疏数据和不同参数梯度差异较大的场景。Adagrad通…...

前端导出带有合并单元格的列表
// 导出async function exportExcel(fileName "共识调整.xlsx") {// 所有数据const exportData await getAllMainData();// 表头内容let fitstTitleList [];const secondTitleList [];allColumns.value.forEach(column > {if (!column.children) {fitstTitleL…...

WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
【HTML-16】深入理解HTML中的块元素与行内元素
HTML元素根据其显示特性可以分为两大类:块元素(Block-level Elements)和行内元素(Inline Elements)。理解这两者的区别对于构建良好的网页布局至关重要。本文将全面解析这两种元素的特性、区别以及实际应用场景。 1. 块元素(Block-level Elements) 1.1 基本特性 …...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)
UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中,UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化…...

华硕a豆14 Air香氛版,美学与科技的馨香融合
在快节奏的现代生活中,我们渴望一个能激发创想、愉悦感官的工作与生活伙伴,它不仅是冰冷的科技工具,更能触动我们内心深处的细腻情感。正是在这样的期许下,华硕a豆14 Air香氛版翩然而至,它以一种前所未有的方式&#x…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...

排序算法总结(C++)
目录 一、稳定性二、排序算法选择、冒泡、插入排序归并排序随机快速排序堆排序基数排序计数排序 三、总结 一、稳定性 排序算法的稳定性是指:同样大小的样本 **(同样大小的数据)**在排序之后不会改变原始的相对次序。 稳定性对基础类型对象…...
Java求职者面试指南:计算机基础与源码原理深度解析
Java求职者面试指南:计算机基础与源码原理深度解析 第一轮提问:基础概念问题 1. 请解释什么是进程和线程的区别? 面试官:进程是程序的一次执行过程,是系统进行资源分配和调度的基本单位;而线程是进程中的…...