初识Caffeine

一、前言:为什么你需要 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 使用软引用(内存不足时回收)

⚠️ 注意maximumSizeweak/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:不设过期时间

  • 问题:脏数据长期滞留
  • 正确做法 :必须设置 expireAfterWriteexpireAfterAccess

❌ 误区 3:用于分布式场景

  • 问题:每个 JVM 实例缓存独立,数据不一致
  • 正确做法本地缓存 + Redis 组成多级缓存

十、结语

感谢您的阅读!如果你有任何疑问或想要分享的经验,请在评论区留言交流!

相关推荐
工业一体机老司机10 小时前
Linux工业一体机自启动服务配置:systemd服务单元编写与开机优化实战
java·linux·服务器
weixin_5386019711 小时前
智能体测开Day56
java
杨丰玮41811 小时前
从零手写Java飞机躲障碍游戏|Swing绘图、鼠标跟随、计时器碰撞检测实战(五)
java·python·游戏·游戏引擎·图形渲染·动画·贴图
莫得感情 o11 小时前
踩坑 - 压测三轮后 502:一个默认 10 的连接池如何拖垮整个服务
java
Escalating_xu11 小时前
【Linux线程】线程控制全解析:终止、join/detach、cancel、线程栈与 NPTL(下篇)
java·linux·运维
2602_9599609212 小时前
电商大厂Java面试实录:Spring Boot/JVM/Redis/Kafka/微服务/安全/测试全解析
java·jvm·spring boot·redis·面试题
吃饱了得干活12 小时前
限界上下文之后:微服务怎么拆、上下文怎么聊?
java·后端·架构
yngsqq12 小时前
窗体快速取消
java·开发语言
岁月如歌778612 小时前
一次讲透 Redis 缓存击穿、穿透、雪崩,从原理到实战解决方案
java·后端·架构
weixin_BYSJ198713 小时前
springboot智慧公共交通系统---附源码51123
java·javascript·spring boot·python·django·flask·php