Java高性能缓存库Caffeine全解析

Caffeine 是一个高性能、接近最优的 Java 本地缓存库,由 Ben Manes 开发,作为 Google Guava Cache 的继任者而广受欢迎。它在设计上融合了现代缓存算法(如 Window TinyLFU)、低开销并发控制和丰富的功能特性,被广泛应用于 Spring Boot、Hibernate、Kafka 等主流框架中。

下面从 核心特性、工作原理、使用方式、最佳实践 四个维度为你系统介绍 Caffeine。


一、为什么选择 Caffeine?(vs. Guava / ConcurrentHashMap)

特性 Caffeine Guava Cache ConcurrentHashMap
淘汰算法 ✅ Window TinyLFU(命中率更高) LRU 近似 ❌ 无自动淘汰
性能 ⚡ 极高(接近理论最优) 良好 高(但需手动管理过期)
自动过期 ✅ 支持 expireAfterWrite / Access / Variable ✅ 支持 ❌ 不支持
异步加载 AsyncLoadingCache
统计监控 ✅ 内置 hit/miss/eviction 统计
权重控制 maximumWeight + weigher
Spring 集成 @Cacheable 原生支持 ✅(但已不推荐)

💡 结论 :Caffeine 是 Guava Cache 的现代化替代品,性能更强、功能更全、维护更活跃。


二、核心特性详解

1. 智能淘汰策略:Window TinyLFU

  • 结合 LFU(频率) + LRU(新鲜度),解决传统 LFU "冷数据长期驻留" 和 LRU "扫描攻击易失效" 的问题。
  • 实测命中率比 LRU 高 20%~50%,尤其适合有"热点数据"的场景(如用户会话、API 响应缓存)。

2. 多种过期策略

java 复制代码
Caffeine.newBuilder()
  .expireAfterWrite(10, TimeUnit.MINUTES)   // 写入后10分钟过期
  .expireAfterAccess(5, TimeUnit.MINUTES)   // 最后访问后5分钟过期
  .expireAfter(new Expiry<String, Object>() {
      public long expireAfterCreate(String key, Object value, long currentTime) {
          return TimeUnit.MINUTES.toNanos(10);
      }
      // 可实现 per-entry 动态过期时间
  })

3. 容量控制

  • maximumSize(n):最多缓存 n 个条目(基于数量)

  • maximumWeight(n) + weigher:按权重(如对象大小、内存占用)控制总容量

    java 复制代码
    .maximumWeight(100_000)
    .weigher((String key, Data value) -> value.sizeInBytes())

4. 异步加载(AsyncLoadingCache)

java 复制代码
AsyncLoadingCache<String, String> cache = Caffeine.newBuilder()
    .buildAsync(key -> fetchDataFromDB(key)); // 返回 CompletableFuture

CompletableFuture<String> future = cache.get("userId");
future.thenAccept(data -> System.out.println(data));

5. 监听器与统计

java 复制代码
Caffeine.newBuilder()
  .removalListener((key, value, cause) -> {
      System.out.println("Removed: " + key + " due to " + cause);
  })
  .recordStats() // 启用统计
  .build();

// 查看命中率
System.out.println(cache.stats().hitRate()); // 如 0.85

三、基本使用方式

1. 手动缓存(Manual Cache)

适用于需要显式 put/get 的场景(如你的文件上传去重):

java 复制代码
Cache<String, Object> cache = Caffeine.newBuilder()
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .maximumSize(10_000)
    .build();

cache.put("key", "value");
Object val = cache.getIfPresent("key");
cache.invalidate("key"); // 删除

2. 自动加载缓存(Loading Cache)

适用于"按需加载 + 自动缓存"的场景(如数据库查询缓存):

java 复制代码
LoadingCache<String, User> userCache = Caffeine.newBuilder()
    .maximumSize(1000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .build(key -> loadUserFromDB(key)); // 自动加载函数

User user = userCache.get("user123"); // 若不存在,自动调用 loadUserFromDB

3. 异步加载缓存(AsyncLoadingCache)

适用于 I/O 密集型操作(如远程 API 调用):

java 复制代码
AsyncLoadingCache<String, Profile> profileCache = Caffeine.newBuilder()
    .buildAsync(this::fetchProfileAsync); // 返回 CompletableFuture<Profile>

四、高级技巧与最佳实践

✅ 1. 线程安全无需担心

  • 所有 Caffeine 缓存都是线程安全 的,内部使用 Striped Lock / Non-blocking design,高并发下性能优异。

✅ 2. 避免在 mappingFunction 中做耗时操作阻塞其他请求

  • LoadingCacheload 函数是同步且串行化 per-key 的:
    • 同一 key 的多个并发请求,只有一个会触发 load,其余等待结果。
    • 但不同 key 之间完全并行。
  • 如果 load 很慢,考虑用 AsyncLoadingCache

✅ 3. 合理设置 maximumSize

  • 不要盲目设大,否则可能 OOM。
  • 建议:根据 QPS × 平均缓存停留时间 估算所需容量。 例:每秒 1000 请求,缓存保留 10 分钟 → 理论最大条目 ≈ 1000 × 600 = 60 万

✅ 4. 结合 Spring Cache 使用

java 复制代码
@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    public CacheManager caffeineCacheManager() {
        CaffeineCacheManager cacheManager = new CaffeineCacheManager();
        cacheManager.setCaffeine(Caffeine.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .maximumSize(1000));
        return cacheManager;
    }
}

@Service
public class UserService {
    @Cacheable("users")
    public User getUser(String id) {
        return db.findById(id);
    }
}

✅ 5. 监控与调优

  • 启用 .recordStats(),定期打印 cache.stats()

    java 复制代码
    CacheStats stats = cache.stats();
    double hitRate = stats.hitRate();        // 命中率
    long evictionCount = stats.evictionCount(); // 淘汰次数
  • 若命中率 < 70%,考虑增大容量或延长过期时间。


五、典型应用场景

场景 推荐用法
防重复提交 / 幂等控制 Manual Cache + expireAfterWrite + putIfAbsent
数据库查询缓存 Loading Cache + expireAfterWrite
远程 API 响应缓存 AsyncLoading Cache + expireAfterWrite
会话/Token 存储 Manual Cache + expireAfterAccess(空闲过期)
高频配置项缓存 Loading Cache + 较长 TTL

六、依赖引入(Maven)

xml 复制代码
<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>3.1.8</version> <!-- 检查最新版 -->
</dependency>

📌 官网:https://github.com/ben-manes/caffeine

📚 文档:https://github.com/ben-manes/caffeine/wiki


总结

Caffeine 是 Java 本地缓存的事实标准,它:

  • 性能顶尖(优于 Guava、Ehcache 本地模式)
  • API 简洁(Manual / Loading / Async 三种模式覆盖所有场景)
  • 功能强大(智能淘汰、动态过期、权重控制、统计监控)
  • 生态完善(Spring、Micronaut、Quarkus 原生支持)

对于你的文件上传去重、接口幂等、热点数据缓存等需求,Caffeine 是轻量、高效、可靠的首选方案。

如需具体场景的代码模板(如分布式锁+本地缓存二级去重),可继续提问!

相关推荐
专注于大数据技术栈2 小时前
java学习--什么是线程安全和不安全
java·学习·安全
进击的小菜鸡dd2 小时前
互联网大厂Java面试:从Spring Boot到微服务架构的场景化技术问答
java·spring boot·redis·ci/cd·微服务·消息队列·mybatis
IT19952 小时前
IDEA+JDK11编译SpringCore5源码及替换项目中的Springcore
java·ide·intellij-idea
techzhi2 小时前
IntelliJ IDEA 启动卡顿问题排查与解决
java·ide·intellij-idea
WayneJoon.H2 小时前
2023CISCN deserbug复现
java·安全·web安全·cc链·反序列化
week_泽2 小时前
第8课:LangGraph Memory管理机制与实现方案 - 学习笔记_8
java·笔记·学习·ai agent
装不满的克莱因瓶2 小时前
【cursor】前后端分离项目下的AI跨工程管理方案
java·人工智能·ai·ai编程·cursor·trae·qoder
何中应2 小时前
使用Spring自带的缓存注解维护数据一致性
java·数据库·spring boot·后端·spring·缓存
ZeroToOneDev2 小时前
Mybatis
java·数据库·mybatis