news 2026/9/29 23:19:36

Caffeine 缓存 详解

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Caffeine 缓存 详解

Caffeine 是Java高性能堆内本地缓存库,Guava Cache 的继任者,Spring5 推荐本地缓存实现,核心亮点是W-TinyLFU淘汰算法,高并发、高命中率,适合单机热点数据缓存。

本质:JVM内存缓存,单机有效,重启丢失,多实例之间数据不自动同步;常和 Redis 组成多级缓存(Caffeine一级,Redis二级)。

一、核心原理:W-TinyLFU(Window TinyLFU)

Guava Cache 用 LRU(只看最近访问),容易被一次性批量查询的冷数据污染缓存;W-TinyLFU同时兼顾访问频率+最近访问时间,命中率接近理论最优 Belady 算法。

三段队列:

  1. Window 窗口队列:新写入数据先进窗口区,保护新热点,防止一次性大量冷数据直接淘汰老热点
  2. Probation 候选淘汰队列:窗口满了移到这里;候选区里对比访问频率,淘汰低频数据
  3. Protected 保护队列:候选区中被再次访问的数据升级到此,尽量保留热点;保护队列满则降级回候选区

频率统计用Count-Min Sketch,用少量内存近似统计访问频次,不存完整计数器,内存开销很小。

并发模型:读操作几乎无锁,写操作CAS + RingBuffer异步批量更新访问统计,极大降低锁竞争,吞吐量远超Guava Cache。

二、三大缓存API

  1. Cache<K,V>:基础缓存,getIfPresent;get(key, loader)不存在时同步加载
  2. LoadingCache<K,V>:继承Cache,自动加载,get不存在时自动调用loader加载
  3. AsyncLoadingCache<K,V>:异步加载,返回CompletableFuture,IO阻塞场景推荐

三、淘汰/过期策略(可组合使用)

1. 基于容量(生产必须配置,防止OOM)

  • maximumSize(long):最大缓存条目数,超限按W-TinyLFU淘汰
  • maximumWeight(long) + weigher():按对象权重淘汰(比如按value字节大小)

2. 基于时间过期

  • expireAfterWrite(5, TimeUnit.MINUTES):写入后5分钟过期(固定TTL)
  • expireAfterAccess(5, TimeUnit.MINUTES):最后一次访问起计时,闲置超时自动清理(适合不常访问数据)
  • refreshAfterWrite(1, TimeUnit.MINUTES):后台异步刷新,访问key时如果超过设定时间,异步去加载新值,旧值先返回,不会阻塞用户请求(缓解缓存雪崩)

    ⚠️ refreshAfterWrite 不是过期,只是主动刷新,不会主动删除旧key

3. 基于引用GC回收(谨慎使用)

  • weakKeys():key弱引用,GC回收
  • weakValues():value弱引用
  • softValues():value软引用,内存不足时JVM自动回收,兜底防OOM

附加能力

  • removalListener:缓存被淘汰/删除时回调(日志、监控)
  • recordStats():开启统计,可获取命中率、命中数、加载耗时、淘汰数量等指标,线上监控必备

四、Maven依赖

<dependency> <groupId>com.github.benmanes.caffeine</groupId> <artifactId>caffeine</artifactId> <version>3.1.8</version> </dependency>

五、基础代码示例

import com.github.benmanes.caffeine.cache.Caffeine; import com.github.benmanes.caffeine.cache.LoadingCache; import java.util.concurrent.TimeUnit; public class CaffeineDemo { public static void main(String[] args) { // LoadingCache:自动加载版本 LoadingCache<String, String> cache = Caffeine.newBuilder() .maximumSize(10000) // 最多1万条 .expireAfterWrite(5, TimeUnit.MINUTES) // 写入5分钟过期 .refreshAfterWrite(1, TimeUnit.MINUTES) // 写入1分钟后,访问时异步刷新 .recordStats() // 开启统计 .removalListener((key, val, cause) -> { System.out.println("缓存移除:key=" + key + ",原因:" + cause); }) // 缓存缺失时加载函数,一般查DB/远程接口 .build(key -> loadFromDb(key)); // 使用 String val = cache.get("user:1001"); System.out.println(val); // 获取统计信息 System.out.println(cache.stats()); } private static String loadFromDb(String key) { // 模拟数据库查询 return key + "_data"; } }

六、适用 & 不适用场景

✅ 适合

  • 单机、低延迟热点数据(配置、字典、基础资料)
  • 读多写少,数据体量可控,可放入JVM堆
  • 多级缓存架构,作为Redis前一级本地缓存,减少Redis压力

❌ 不适合

  • 分布式多实例要求缓存强一致(各实例本地缓存独立,更新会脏读)
  • 超大数据集,无法全部放入JVM堆
  • 需要持久化、跨服务共享缓存(用Redis)

多级缓存经典链路:请求 → Caffeine本地缓存命中直接返回 → 未命中查Redis → Redis未命中查DB,回写Redis + Caffeine

七、Caffeine vs Guava Cache vs Redis

CaffeineGuava CacheRedis
位置JVM本地内存JVM本地内存独立中间件,网络访问
淘汰算法W-TinyLFULRULRU / LFU等
性能极高中等受网络影响
多实例共享❌ 不共享❌ 不共享✅ 全局共享
持久化❌❌✅ RDB/AOF

八、生产避坑重点

  1. 必须设置maximumSize,只配置expire不设上限,会无限涨内存导致OOM;
  2. 分布式场景,Caffeine只做短期兜底缓存,配合消息广播/Canal主动失效本地缓存,同时TTL做兜底,容忍短暂脏读;
  3. refreshAfterWrite只是异步刷新,不能解决缓存穿透,空值记得缓存;
  4. 缓存key最好不可变,正确重写equals/hashCode;
  5. 统计指标(命中率)一定要监控,命中率过低说明缓存策略不合理;
  6. 软引用softValues慎用,GC频繁会严重影响性能,优先用maximumSize控制。

九、Caffeine 缓存(Java本地缓存库)最新正式版:3.2.4(2026-05-04 发布)

项目地址:GitHub - ben-manes/caffeine: A high performance caching library for Java · GitHub

Maven

<dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> <version>3.2.4</version> </dependency>

Gradle

implementation 'com.github.ben-manes.caffeine:caffeine:3.2.4'

Kotlin DSL

implementation("com.github.ben-manes.caffeine:caffeine:3.2.4")

📌 JDK版本硬性要求

  • 3.x 系列:JDK 11+(3.2.4 需要 Java11 及以上)
  • 2.x 系列:JDK 8,最新稳定2.9.3,老Java8项目用这个

配套扩展包(同版本)

<!-- JSR107 JCache规范实现,需要JCache注解时引入 --> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>jcache</artifactId> <version>3.2.4</version> </dependency> <!-- Guava适配器,兼容Guava Cache代码迁移 --> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>guava</artifactId> <version>3.2.4</version> </dependency>

版本选择建议

  1. SpringBoot3 / JDK17/21新项目 →3.2.4
  2. SpringBoot2、JDK8老项目 →2.9.3(不要强行上3.x)
  3. 生产优先用正式Release,不要用SNAPSHOT快照版

之前示例写的 3.1.8 属于旧版,建议新项目升级到3.2.4,修复了一些并发、内存统计的bug。

十、SpringBoot 4.x + Caffeine 支持说明

✅Spring Boot 4.x 原生完整支持 Caffeine,内置CaffeineCacheManager自动装配,@Cacheable、@CacheEvict等 Spring Cache 注解全部可用,和 SpringBoot3 使用方式几乎一样。

SpringBoot4 基线:JDK17+,正好匹配 Caffeine 3.x 的最低要求(JDK11+),推荐直接用Caffeine 3.2.4

1、SpringBoot4 托管版本

SpringBoot4 的 dependency-management 自带管理 Caffeine 3.2.x,不写 version 也能自动拉取对应版本; 你也可以显式指定3.2.4(当前最新正式版),完全兼容。

Maven依赖

<!-- Spring Cache starter --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency> <!-- Caffeine本地缓存 --> <dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> <version>3.2.4</version> </dependency>

Gradle

implementation 'org.springframework.boot:spring-boot-starter-cache' implementation 'com.github.ben-manes.caffeine:caffeine:3.2.4'

2、两种配置方式(SpringBoot4通用)

方式1:yaml配置(简单场景,CaffeineSpec语法)

spring: cache: type: caffeine cache-names: userDict,areaDict caffeine: spec: maximumSize=10000,expireAfterWrite=5m,refreshAfterWrite=1m

spec参数说明:最大1w条,写入5分钟过期,访问时1分钟后异步后台刷新

方式2:自定义Caffeine Bean(推荐,可自定义removalListener、统计、复杂逻辑)

import com.github.benmanes.caffeine.cache.Caffeine; import org.springframework.cache.CacheManager; import org.springframework.cache.caffeine.CaffeineCacheManager; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.cache.annotation.EnableCaching; import java.util.concurrent.TimeUnit; @Configuration @EnableCaching public class CaffeineCacheConfig { @Bean public CacheManager cacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); // 构造Caffeine实例 Caffeine<Object, Object> caffeineBuilder = Caffeine.newBuilder() .maximumSize(10000) .expireAfterWrite(5, TimeUnit.MINUTES) .refreshAfterWrite(1, TimeUnit.MINUTES) .recordStats() // 开启缓存指标统计(命中率、淘汰数) .removalListener((key, val, cause) -> { // 缓存淘汰回调,可埋点日志 }); cacheManager.setCaffeine(caffeineBuilder); // 可选:不允许缓存null,防止缓存穿透 cacheManager.setAllowNullValues(false); return cacheManager; } }

3、SpringBoot4 注意事项

  1. JDK版本底线:SpringBoot4 最低 JDK17,Caffeine3.2.4 在 JDK17/21/23 全部稳定,不要用Caffeine2.x(2.x是JDK8版本,不建议在Boot4混用)
  2. 自动装配逻辑不变:classpath存在caffeine包,SpringBoot自动实例化CaffeineCacheManager;手动声明CacheManager Bean则优先使用自定义Bean
  3. GraalVM Native镜像:SpringBoot4支持native打包,Caffeine3.x对GraalVM支持良好,少量场景需要添加native hint
  4. 注解使用和Boot3完全一致
@Cacheable(value = "userDict", key = "#id") public UserDTO getUser(Long id) { // 查DB逻辑 }

4、常见坑

  1. refreshAfterWrite:仅访问时触发异步刷新,不会主动后台轮询刷新,旧值会继续返回,不会阻塞请求
  2. setAllowNullValues(false):开启后方法返回null会抛异常,想缓存空值来防穿透,需要设置true;
  3. Caffeine是单机本地缓存,分布式环境多实例会数据不一致,适合多级缓存(Caffeine + Redis)架构;

5、版本选型建议

  • SpringBoot4.0 /4.1 → Caffeine3.2.4(推荐)
  • 不要混用 caffeine 2.x,2.x面向JDK8,在JDK17+虽然能跑,但缺少新特性和bug修复
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/29 23:17:29

Mobius大模型FP16稳定运行指南:rescale_every权重缩放机制揭秘

Mobius大模型FP16稳定运行指南&#xff1a;rescale_every权重缩放机制揭秘 【免费下载链接】Mobius大模型 Mobius大模型是开放原子基金会开源项目&#xff0c;采用OpenAtom-Model-License-V1.0协议。具备强大的自然语言理解与生成能力&#xff0c;支持多场景应用开发&#xff0…

作者头像 李华
网站建设 2026/9/29 23:17:28

GitHub 开源热榜项目-周榜(2026-09-28)

&#x1f525; GitHub 开源热榜项目 (2026-09-21 ~ 2026-09-27) &#x1f4c5; 周期&#xff1a;2026-09-21 ~ 2026-09-27 | &#x1f550; 更新&#xff1a;2026-09-28 共收录 09 个项目&#xff0c;按 Star 数排序 &#x1f4ca; 本期概览 本期榜单&#xff08;2026-09-21 ~…

作者头像 李华
网站建设 2026/9/29 23:17:28

2025 年使用 SeleniumBase 和 Python 进行网页抓取

在本文中&#xff0c;我将带你了解 SeleniumBase 的核心功能&#xff0c;展示如何用它进行抓取&#xff0c;并分享一些应对当今网络挑战的实用技巧。无论你是刚接触抓取&#xff0c;还是希望进一步提升技能&#xff0c;SeleniumBase 都提供了一种简单直接的入门方式。 什么是 …

作者头像 李华
网站建设 2026/9/29 23:17:20

OpenClaw + Hermes + Vibe Coding:从零搭建你的“终极AI科研工作台”——模型部署、Agent编程、论文写作与知识管理全链路实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

作者头像 李华