一、前言:为什么你需要 Caffeine?
你是否遇到过这些问题?
- ❌ 频繁查询数据库,接口响应慢
- ❌ 热点数据反复被请求,浪费资源
- ❌ 手写
ConcurrentHashMap缓存,结果内存爆炸(OOM)
Caffeine 是一个高性能、易用、现代化的 Java 本地缓存库 ,由 Guava Cache 的原作者打造,号称 "Guava Cache 的继任者"。
它能帮你:
✅ 微秒级数据访问 (比 Redis 快 1000 倍)
✅ 自动淘汰旧数据 ,防止内存溢出
✅ 一行代码实现缓存加载
本文将带你从零开始,快速上手 Caffeine!
二、Caffeine 是什么?
Caffeine 是一个基于 Java 8+ 的高性能进程内缓存库,特点如下:
| 特性 | 说明 |
|---|---|
| 高性能 | 采用 Window TinyLFU 淘汰算法,命中率更高 |
| 简单易用 | API 与 Guava Cache 高度相似,学习成本低 |
| 功能丰富 | 支持自动过期、异步加载、统计监控等 |
| 社区活跃 | Spring Boot 3.x 官方推荐本地缓存方案 |
💡 一句话理解 :Caffeine = 更快 + 更省内存 + 更智能 的
ConcurrentHashMap
三、快速入门:5 分钟上手 Caffeine
3.1 添加 Maven 依赖
XML
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactityId>caffeine</artifactId>
<version>3.1.8</version> <!-- 推荐使用最新版 -->
</dependency>
3.2 基础用法:手动存取
java
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
public class CaffeineDemo {
public static void main(String[] args) {
// 创建缓存:最多存 1000 个条目,写入 5 分钟后过期
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.build();
// 存入数据
cache.put("name", "Alice");
// 读取数据
String name = cache.getIfPresent("name");
System.out.println(name); // 输出:Alice
// 不存在时返回 null
String notExist = cache.getIfPresent("age");
System.out.println(notExist); // 输出:null
}
}
四、高级用法:自动加载缓存(LoadingCache)
最强大的功能来了!当缓存未命中时,自动从数据源加载。
4.1 自动回源查询数据库
java
LoadingCache<Long, User> userCache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> {
// 模拟从数据库加载
System.out.println("从数据库加载用户: " + key);
return loadUserFromDB(key); // 你的 DAO 方法
});
// 第一次调用:触发 loadUserFromDB
User user1 = userCache.get(1001L);
// 第二次调用:直接从缓存返回
User user2 = userCache.get(1001L);
✅ 效果:业务代码无需关心"先查缓存再查 DB"的逻辑,Caffeine 自动搞定!
五、核心配置详解
5.1 淘汰策略
| 配置 | 作用 |
|---|---|
.maximumSize(1000) |
最多缓存 1000 个 key-value |
.expireAfterWrite(5, MINUTES) |
写入后 5 分钟过期 |
.expireAfterAccess(10, MINUTES) |
最后访问后 10 分钟过期 |
.weakKeys() |
key 使用弱引用(GC 可回收) |
.softValues() |
value 使用软引用(内存不足时回收) |
⚠️ 注意 :
maximumSize和weak/soft不能同时使用。
5.2 异步加载(AsyncLoadingCache)
java
AsyncLoadingCache<String, String> asyncCache = Caffeine.newBuilder()
.buildAsync((key, executor) ->
CompletableFuture.supplyAsync(() -> fetchDataFromRemote(key))
);
CompletableFuture<String> future = asyncCache.get("data");
String result = future.get(); // 阻塞等待结果
六、监控缓存性能
Caffeine 内置统计功能,轻松查看命中率:
java
Cache<String, Object> cache = Caffeine.newBuilder()
.recordStats() // 开启统计
.build();
// ... 使用缓存 ...
CacheStats stats = cache.stats();
System.out.println("命中率: " + stats.hitRate()); // 如 0.92
System.out.println("加载次数: " + stats.loadCount()); // 数据源调用次数
📊 生产建议:将命中率接入 Prometheus + Grafana,实时监控缓存健康度。
七、Caffeine vs Guava Cache
| 对比项 | Guava Cache | Caffeine |
|---|---|---|
| 性能 | 一般 | 更高(TinyLFU 算法) |
| 内存占用 | 较高 | 更低 |
| 功能 | 基础 | 更丰富(异步、权重等) |
| 维护状态 | 停滞 | 活跃更新 |
| Spring 支持 | 需手动集成 | Spring Boot 3.x 官方支持 |
✅ 结论 :新项目请直接使用 Caffeine!
八、在 Spring Boot 中使用 Caffeine
8.1 配置 Bean
java
@Configuration
public class CacheConfig {
@Bean
public Cache<String, User> userCache() {
return Caffeine.newBuilder()
.maximumSize(5000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build();
}
}
8.2 在 Service 中注入使用
java
@Service
public class UserService {
@Autowired
private Cache<String, User> userCache;
public User getUser(String id) {
return userCache.get(id, this::loadUserFromDB);
}
private User loadUserFromDB(String id) {
// 模拟 DB 查询
return new User(id, "Name-" + id);
}
}
💡 提示 :Spring 6+ 已内置
CaffeineCacheManager,可直接用@Cacheable注解。
九、常见误区与避坑指南
❌ 误区 1:缓存所有数据
- 问题:内存爆炸(OOM)
- 正确做法 :只缓存热点、小、读多写少的数据
❌ 误区 2:不设过期时间
- 问题:脏数据长期滞留
- 正确做法 :必须设置
expireAfterWrite或expireAfterAccess
❌ 误区 3:用于分布式场景
- 问题:每个 JVM 实例缓存独立,数据不一致
- 正确做法 :本地缓存 + Redis 组成多级缓存
十、结语
感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!