Caffeine 是Java高性能堆内本地缓存库,Guava Cache 的继任者,Spring5 推荐本地缓存实现,核心亮点是W-TinyLFU淘汰算法,高并发、高命中率,适合单机热点数据缓存。
本质:JVM内存缓存,单机有效,重启丢失,多实例之间数据不自动同步;常和 Redis 组成多级缓存(Caffeine一级,Redis二级)。
一、核心原理:W-TinyLFU(Window TinyLFU)
Guava Cache 用 LRU(只看最近访问),容易被一次性批量查询的冷数据污染缓存;W-TinyLFU同时兼顾访问频率+最近访问时间,命中率接近理论最优 Belady 算法。
三段队列:
- Window 窗口队列:新写入数据先进窗口区,保护新热点,防止一次性大量冷数据直接淘汰老热点
- Probation 候选淘汰队列:窗口满了移到这里;候选区里对比访问频率,淘汰低频数据
- Protected 保护队列:候选区中被再次访问的数据升级到此,尽量保留热点;保护队列满则降级回候选区
频率统计用Count-Min Sketch,用少量内存近似统计访问频次,不存完整计数器,内存开销很小。
并发模型:读操作几乎无锁,写操作CAS + RingBuffer异步批量更新访问统计,极大降低锁竞争,吞吐量远超Guava Cache。
二、三大缓存API
Cache<K,V>:基础缓存,getIfPresent;get(key, loader)不存在时同步加载LoadingCache<K,V>:继承Cache,自动加载,get不存在时自动调用loader加载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
| Caffeine | Guava Cache | Redis | |
|---|---|---|---|
| 位置 | JVM本地内存 | JVM本地内存 | 独立中间件,网络访问 |
| 淘汰算法 | W-TinyLFU | LRU | LRU / LFU等 |
| 性能 | 极高 | 中等 | 受网络影响 |
| 多实例共享 | ❌ 不共享 | ❌ 不共享 | ✅ 全局共享 |
| 持久化 | ❌ | ❌ | ✅ RDB/AOF |
八、生产避坑重点
- 必须设置maximumSize,只配置expire不设上限,会无限涨内存导致OOM;
- 分布式场景,Caffeine只做短期兜底缓存,配合消息广播/Canal主动失效本地缓存,同时TTL做兜底,容忍短暂脏读;
refreshAfterWrite只是异步刷新,不能解决缓存穿透,空值记得缓存;- 缓存key最好不可变,正确重写equals/hashCode;
- 统计指标(命中率)一定要监控,命中率过低说明缓存策略不合理;
- 软引用
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>版本选择建议
- SpringBoot3 / JDK17/21新项目 →3.2.4
- SpringBoot2、JDK8老项目 →2.9.3(不要强行上3.x)
- 生产优先用正式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=1mspec参数说明:最大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 注意事项
- JDK版本底线:SpringBoot4 最低 JDK17,Caffeine3.2.4 在 JDK17/21/23 全部稳定,不要用Caffeine2.x(2.x是JDK8版本,不建议在Boot4混用)
- 自动装配逻辑不变:classpath存在caffeine包,SpringBoot自动实例化
CaffeineCacheManager;手动声明CacheManager Bean则优先使用自定义Bean - GraalVM Native镜像:SpringBoot4支持native打包,Caffeine3.x对GraalVM支持良好,少量场景需要添加native hint
- 注解使用和Boot3完全一致
@Cacheable(value = "userDict", key = "#id") public UserDTO getUser(Long id) { // 查DB逻辑 }4、常见坑
refreshAfterWrite:仅访问时触发异步刷新,不会主动后台轮询刷新,旧值会继续返回,不会阻塞请求setAllowNullValues(false):开启后方法返回null会抛异常,想缓存空值来防穿透,需要设置true;- Caffeine是单机本地缓存,分布式环境多实例会数据不一致,适合多级缓存(Caffeine + Redis)架构;
5、版本选型建议
- SpringBoot4.0 /4.1 → Caffeine3.2.4(推荐)
- 不要混用 caffeine 2.x,2.x面向JDK8,在JDK17+虽然能跑,但缺少新特性和bug修复