🚀 整合Spring Cache:本地缓存、Redis与Caffeine对比实践

📌 前言

在高并发、高性能的系统设计中,缓存始终扮演着不可替代的角色。Spring Cache 作为 Spring 框架原生提供的缓存抽象层,极大简化了缓存接入的复杂度。然而,如何选择合适的缓存组件?如何支持多级缓存?如何处理缓存一致性和失效问题?这些才是“实战”真正的挑战。

文章目录

  • 🚀 整合Spring Cache:本地缓存、Redis与Caffeine对比实践
    • 📌 前言
  • 🔍 一、Spring Cache注解驱动原理
    • 💡 核心注解解析
    • ⚙️ 核心源码解析
    • 🔑 缓存Key生成机制
    • 🎯 SpEL表达式高级用法
  • 📊 二、缓存方案对比分析
    • 💡 主流缓存方案对比
    • ⚡️ 性能对比数据(单操作)
    • 🔄 选型决策树
  • ⚙️ 三、Caffeine深度解析
    • 💡 Caffeine vs 其他本地缓存
    • ⚡️ Caffeine配置模板
    • 📈 命中率监控实战
  • 🚀 四、混合缓存架构实战
    • 💡 多级缓存架构设计
    • ⚙️ 自定义二级缓存实现
    • 🔄 缓存失效一致性方案
    • ⚡️ Redis消息监听实现
  • 🧩 五、缓存陷阱与优化实践
    • 💣 三大缓存问题解决方案
    • ⚡️ 缓存预热实现
    • 📌 TTL设计黄金法则
    • 🚨 实战踩坑记录
  • 💎 六、最佳实践总结
    • 🏆 混合缓存架构设计
    • 📜 缓存使用军规
    • 🛠 推荐工具栈

🔍 一、Spring Cache注解驱动原理

💡 核心注解解析

ClientAOP代理CacheManagerCacheTarget调用@Cacheable方法获取Cache实例查询缓存返回缓存值直接返回结果执行目标方法返回结果获取Cache实例存储结果返回结果alt[缓存命中][缓存未命中]ClientAOP代理CacheManagerCacheTarget

⚙️ 核心源码解析

// CacheAspectSupport.execute()
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {// 1. 检查是否开启缓存if (contexts.isSynchronized()) {// 同步处理...}// 2. 处理@Cacheableif (contexts.get(CacheableOperation.class).isEmpty()) {return invokeOperation(invoker);}// 3. 缓存查找逻辑Cache.ValueWrapper cacheHit = findCachedItem(contexts.get(CacheableOperation.class));// 4. 缓存未命中时调用实际方法if (cacheHit == null) {return execute(invoker, contexts);}
}

🔑 缓存Key生成机制

// SimpleKeyGenerator 默认实现
public Object generate(Object target, Method method, Object... params) {if (params.length == 0) {return SimpleKey.EMPTY;}if (params.length == 1) {Object param = params[0];return (param != null ? param : SimpleKey.EMPTY);}return new SimpleKey(params); // 多参数组合
}

🎯 SpEL表达式高级用法

// 动态Key生成
@Cacheable(value="users", key="#user.id + '_' + #user.type")
public User getUser(User user) {// ...
}// 条件缓存
@Cacheable(value="orders", condition="#amount > 1000")
public Order getOrder(Long id, BigDecimal amount) {// ...
}// 结果影响缓存策略
@CachePut(value="users", unless="#result.status == 'LOCKED'")
public User updateUser(User user) {// ...
}

📊 二、缓存方案对比分析

💡 主流缓存方案对比

特性RedisCaffeineConcurrentHashMap适用场景
​​分布式​​集群环境
​​性能​​0.1ms级0.01ms级0.005ms级高频访问
​​内存管理​​独立服务器JVM堆内存JVM堆内存内存敏感
淘汰策略​​LRU/LFU等WindowTinyLFU无自动淘汰
​​持久化​​数据持久化
​​事务支持​​复杂操作
​​**命中率​​ **依赖内存大小98%+100%(无淘汰)热点数据

⚡️ 性能对比数据(单操作)

操作RedisCaffeineConcurrentHashMap
GET0.1-1ms0.01-0.05ms0.005-0.01ms
PUT0.2-2ms0.02-0.1ms0.01-0.05ms
10kQPS内存 独立服务器200-500MB50-200MB

🔄 选型决策树

需求分析
是否跨节点共享
Redis
数据量大小
Caffeine
Redis
分布式系统
单机热点数据
大数据量缓存

⚙️ 三、Caffeine深度解析

💡 Caffeine vs 其他本地缓存

特性CaffeineGuava CacheEhcache
淘汰算法​​WindowTinyLFULRU
命中率​​★★★★★★★★☆☆★★★★☆
​​并发性能​​ ★★★★★★★★★☆★★★☆☆
​​内存控制​​ 权重支持支持支持
​​监控能力​​内置统计需扩展内置

⚡️ Caffeine配置模板

@Configuration
public class CacheConfig {@Beanpublic CaffeineCacheManager cacheManager() {Caffeine<Object, Object> caffeine = Caffeine.newBuilder().initialCapacity(200) // 初始大小.maximumSize(1000)    // 最大条目.expireAfterWrite(10, TimeUnit.MINUTES) // 写入后过期时间.refreshAfterWrite(1, TimeUnit.MINUTES) // 刷新间隔.recordStats();       // 开启统计CaffeineCacheManager manager = new CaffeineCacheManager();manager.setCaffeine(caffeine);return manager;}
}

📈 命中率监控实战

@Autowired
private CacheManager cacheManager;@Scheduled(fixedRate = 30_000)
public void logCacheStats() {CaffeineCache cache = (CaffeineCache) cacheManager.getCache("users");com.github.benmanes.caffeine.cache.stats.CacheStats stats = cache.getNativeCache().stats();log.info("命中率: {}/{} 平均加载时间: {}ms", stats.hitCount(), stats.requestCount(),stats.averageLoadPenalty() / 1_000_000);
}

🚀 四、混合缓存架构实战

💡 多级缓存架构设计

失效同步
命中
未命中
命中
未命中
Redis发布消息
数据库查询
节点清理本地缓存
客户端请求
本地缓存
直接返回
Redis缓存
返回并回填本地
写入Redis
写入本地缓存
返回结果

⚙️ 自定义二级缓存实现

public class TwoLevelCacheManager implements CacheManager {private final CacheManager localCacheManager;private final CacheManager remoteCacheManager;@Overridepublic Cache getCache(String name) {return new TwoLevelCache(localCacheManager.getCache(name),remoteCacheManager.getCache(name));}
}public class TwoLevelCache implements Cache {private final Cache localCache;private final Cache remoteCache;@Overridepublic ValueWrapper get(Object key) {// 1. 检查本地缓存ValueWrapper value = localCache.get(key);if (value != null) {return value;}// 2. 检查远程缓存value = remoteCache.get(key);if (value != null) {// 3. 回填本地缓存localCache.put(key, value.get());return value;}return null;}@Overridepublic void put(Object key, Object value) {// 双写策略remoteCache.put(key, value);localCache.put(key, value);}
}

🔄 缓存失效一致性方案

节点ARedis节点B1. 更新数据2. 发布缓存失效事件3. 推送失效消息4. 清除本地缓存5. 确认处理节点ARedis节点B

⚡️ Redis消息监听实现

@Configuration
public class CacheEvictConfig {@Beanpublic RedisMessageListenerContainer container(RedisConnectionFactory factory) {RedisMessageListenerContainer container = new RedisMessageListenerContainer();container.setConnectionFactory(factory);container.addMessageListener(messageListener(), new PatternTopic("cache_evict"));return container;}private MessageListener messageListener() {return (message, pattern) -> {String key = new String(message.getBody());// 获取所有本地缓存实例并清除cacheManager.getCacheNames().forEach(name -> {cacheManager.getCache(name).evict(key);});};}
}

🧩 五、缓存陷阱与优化实践

💣 三大缓存问题解决方案

问题现象解决方案实践案例
缓存穿透大量请求不存在的 key1. 布隆过滤器
2. 空值缓存
3. 参数校验
拦截无效用户 ID 查询
缓存雪崩大量 key 同时失效1. 随机 TTL
2. 热点数据永不过期
3. 集群部署
商品列表缓存设置随机过期
缓存击穿热点 key 失效瞬间高并发1. 互斥锁重建
2. 逻辑过期
3. 永不过期
秒杀商品使用分布式锁重建

⚡️ 缓存预热实现

@Component
public class CacheWarmer {@Autowiredprivate ProductService productService;@Autowiredprivate CacheManager cacheManager;@PostConstructpublic void warmUpCache() {List<Product> hotProducts = productService.getTop100HotProducts();Cache cache = cacheManager.getCache("products");hotProducts.forEach(product -> {cache.put(product.getId(), product);});log.info("预热{}条产品数据", hotProducts.size());}
}

📌 TTL设计黄金法则

1.​​基础数据​​:24小时(如分类信息)
2.​​业务数据​​:5-30分钟(如库存信息)
3.​​会话数据​​:30秒-5分钟(如验证码)
​​4.实时数据​​:1-5秒(如秒杀库存)

🚨 实战踩坑记录

  1. 本地缓存内存溢出​​
    • 现象:频繁Full GC导致服务不可用
    • 原因:Caffeine未设置最大条目限制
    • 解决:添加.maximumSize(10000)配置
  2. 缓存不一致​​
    • 现象:DB更新后本地缓存未失效
    • 原因:Redis消息丢失
    • 解决:添加消息确认机制 + 定时全量刷新
  3. 雪崩效应​​
    • 现象:大促期间缓存集体失效
    • 原因:固定TTL配置
    • 解决:基础TTL + 随机偏移量(TTL * (1 + Math.random() * 0.2))

💎 六、最佳实践总结

🏆 混合缓存架构设计

监控
失效同步
采集指标
Prometheus
Grafana展示
Redis发布消息
数据库
节点清理本地缓存
客户端
Caffeine本地缓存
是否命中
返回数据
Redis集群
是否命中
回填本地缓存
写入Redis
写入本地缓存

📜 缓存使用军规

  1. ​​键设计规范​​:
    • 业务前缀:唯一标识(product:123)
    • 版本控制(v1:user:456)
  2. ​​值优化策略​​:
    • 使用Protobuf/MessagePack序列化
    • 大对象压缩存储
  3. ​​写策略​​:
    • 先更新DB再删除缓存
    • 失败重试队列保障
  4. ​​读策略​​:
    • 缓存未命中时加锁重建
    • 熔断降级机制

🛠 推荐工具栈

  1. ​​本地缓存​​:Caffeine(性能王者) ​​
  2. 分布式缓存​​:Redis Cluster
  3. (高可用) ​​监控系统​​:
    • Micrometer + Prometheus
    • Redis Stat
    • ​​压测工具​​:JMeter + Gatling

记住:​​没有完美的缓存方案,只有适合业务场景的平衡选择​​

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/91091.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/91091.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/91091.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

easyexcel填充方式导出-合并单元格并设置边框

填充的模板最后导出效果实体 /*** 账户实体类* author test* date 2025-07-28*/ Getter Setter class Test {/*** 账户类型*/private String accType;/*** 账户余额*/private String money; }导出逻辑 /*** 导出文件逻辑*/ public void exportReport(List<Test> data) { …

Jenkins + SonarQube 从原理到实战一:基于 K8s 部署与使用(含中文插件与 Python 扫描)

前言 公司开发部门希望在 Jenkins 构建过程中自动集成 C/C 的代码扫描&#xff0c;正好我也没接触过 SonarQube&#xff0c;于是记录下从零开始部署 SonarQube 服务并集成到 CI/CD 的过程&#xff0c;供后来者参考。 一、SonarQube 原理与工作机制详解 1.1 什么是 SonarQube&…

Linux(Centos 7.6)命令详解:sz

1.命令作用使用ZMODEM/YMODEM/XMODEM协议发送文件(Send file(s) with ZMODEM/YMODEM/XMODEM protocol)注意: 需要yum install lrzsz (yum provides sz可以查看rz命令是什么rpm包提供的)2.命令语法Usage: sz [options] file ...or: sz [options] -{c|i} COMMAND3.参数详解OPTION…

智能运维中的数据转换

《智能运维实践 苏娜 孙琳 王鸽著 人工智能技术丛书 自然语言处理的常用算法 日志异常检测 根因定位 网络流量异常检测 清华大学出版社》【摘要 书评 试读】- 京东图书 数据转换是数据预处理中的关键步骤&#xff0c;用于将数据从原始格式转换为适合分析和建模的形式。这一过程…

IAR编辑器如何让左侧的工具栏显示出来?

在IAR编辑器中恢复左侧工具栏显示&#xff0c;可通过以下方法操作&#xff1a; 一、通过菜单栏启用工具栏 ‌进入视图菜单‌ 点击顶部菜单栏的 ‌"View"‌ → 在弹出列表中勾选 ‌"Workspace"‌ 若工具栏仍不显示&#xff0c;查看菜单栏右侧是否有 ‌"…

ADB+Python控制(有线/无线) Scrcpy+按键映射(推荐)

要实现电脑通过键盘控制安卓平板屏幕点击的功能&#xff0c;可以采用以下方案&#xff1a; 方案一&#xff1a;ADBPython控制&#xff08;有线/无线&#xff09; 准备工具&#xff1a; 安卓平板开启开发者模式&#xff08;设置→关于平板→连续点击版本号&#xff09;启用USB调…

同态滤波算法详解:基于频域变换的光照不均匀校正

&#x1f3ad; 同态滤波&#xff1a;图像频域的调音师技术“如同调音师在音频处理中分离并调节不同频率成分&#xff0c;同态滤波能够在图像频域中精确分离光照与细节信息。”&#x1f3af; 图像频域调音的技术挑战 在数字图像处理中&#xff0c;光照不均匀问题如同音频中的混响…

Ubuntu简述及部署系统

1.什么是Ubuntu1.1概述Ubuntu属于Debian系列&#xff0c;Debian是社区类Linux的典范&#xff0c;是迄今为止最遵循GNU规范的Linux系统。Debain最早由lan Murdock于1993年创建&#xff0c;分为三个版本分支&#xff08;branch&#xff09;&#xff1a;stable&#xff0c;testing…

Claude Code安装部署

1️⃣安装 Node.js&#xff08;已安装可跳过&#xff09; 确保 Node.js 版本 ≥ 18.0 # Ubuntu / Debian 用户 curl -fsSL https://deb.nodesource.com/setup_lts.x | sudo bash - sudo apt-get install -y nodejs node --version# macOS 用户 sudo xcode-select --install /b…

MATLAB近红外光谱分析技术及实践技术应用

专题一、MATLAB编程基础与进阶&#xff08;一&#xff09;1、MATLAB 安装、版本历史与编程环境2、MATLAB 基础操作&#xff08;矩阵操作、逻辑与流程控制、函数与脚本文件&#xff09;3、MATLAB文件读写&#xff08;mat、txt、xls、csv、jpg、wav、avi等格式&#xff09;专题二…

SQLFluff

一、SQLFluff 是什么&#xff1f; SQLFluff 是一个​​开源的 SQL 代码质量工具​​&#xff0c;专注于通过自动化方式提升 SQL 代码的可读性、一致性和规范性。其核心功能包括&#xff1a; ​​代码格式化​​&#xff1a;自动调整缩进、空格、换行等格式问题&#xff0c;支…

盲盒抽卡机小程序系统开发:连接线上线下娱乐新桥梁

在互联网技术的推动下&#xff0c;线上线下融合已经成为娱乐行业发展的必然趋势。盲盒抽卡机&#xff0c;这一原本在线下备受欢迎的娱乐项目&#xff0c;通过小程序系统的开发&#xff0c;成功实现了线上线下的无缝对接&#xff0c;成为连接线上线下娱乐的新桥梁。盲盒抽卡机小…

【SSL证书校验问题】通过 monkey-patch 关掉 SSL 证书校验

标签&#xff1a;Python、SSL、monkey-patch、httpx、aiohttp、requests、OpenAI 1 为什么会有这篇文章&#xff1f; 在本地调试 OpenAI 代理、数据抓取、私有服务、访问外网 时&#xff0c;经常会碰到如下报错&#xff1a; SSLCertVerificationError: [SSL: CERTIFICATE_VER…

VMWARE -ESXI-ntp时间同步无法启动异常处理

从服务界面查看NTP服务是停止的&#xff08;手动启动无效&#xff09;尝试到系统-时间设置-添加服务-网络时间协议&#xff0c;添加阿里云NTP服务器&#xff08;网络可达&#xff09; ntp.aliyun.com 点击确定报错-无法更改主机配置出现上面的情况多半是DNS没有设置ssh登录到服…

yolo11分类一键训练工具免安装环境windows版使用教程

这个是windows上用于yolo11分类训练工具&#xff0c;不需要写代码只需要按照要求摆放好分类图片文件夹就可以训练。软件内置cuda和python模块&#xff0c;因此不需要安装python环境和cuda就可以使用。注意需要电脑配备有nvidia显卡才能使用。要求显存>4GB。首先我们打开软件…

2025年-ClickHouse 高性能实时分析数据库(大纲版)

告别等待&#xff0c;秒级响应&#xff01;这不只是教程&#xff0c;这是你驾驭PB级数据的超能力&#xff01;我的ClickHouse视频课&#xff0c;凝练十年实战精华&#xff0c;从入门到精通&#xff0c;从单机到集群。点开它&#xff0c;让数据处理速度快到飞起&#xff0c;让你…

深入理解 Scikit-learn:机器学习实战的科学之道与避坑指南

掌握工具易&#xff0c;领悟其道难——本文带你穿透API表面&#xff0c;直击工业级机器学习实践的核心逻辑。作为一名长期耕耘在机器学习研究与工业应用一线的从业者&#xff0c;我见过太多因误用 sklearn 而导致的模型失效案例。从数据泄露到评估失真&#xff0c;从特征处理失…

Android 调试桥 (adb) 基础知识点

Android 调试桥 (adb) 是一种功能多样的命令行工具&#xff0c;可让您与安卓手机进行通信。常用于辅助测试开发或定位问题。 一、adb的工作原理 &#x1f31f; 启动服务器进程&#xff1a;检查是否adb服务器进程正在运行&#xff0c;若没有则启动。 启动或关闭adb服务&#xff…

AW2013 LED驱动芯片 工作方式介绍

根据您上传的 AW2013 芯片手册&#xff0c;我将为您分三个部分详细解读&#xff1a;一、芯片简介&#xff08;AW2013 概述&#xff09; AW2013 是一款 3 通道的 IC 控制恒流 LED 驱动芯片&#xff0c;支持以下功能&#xff1a;单通道最大恒流输出&#xff1a;15mA&#xff0c;支…

node.js中的fs与path模块

前言 什么是Node.js&#xff1f; Node.js 是一个开源和跨平台的 JavaScript 运行时环境 Node.js 在浏览器之外运行 V8 JavaScript 引擎&#xff0c;即 Google Chrome 的核心。一、fs模块基础 1. 引入fs模块 const fs require(fs);2. 同步与异步方法 fs模块提供了同步和异步两种…