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 是轻量、高效、可靠的首选方案。

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

相关推荐
budingxiaomoli12 小时前
SpringCloud概述
java·spring cloud·微服务
绿草在线12 小时前
基于SpringBoot4+Mybatis+Thymeleaf的用户管理系统开发实战
java·spring boot·thymeleaf
Rust研习社12 小时前
Rust 高性能内存缓存 moka 完全指南
开发语言·后端·缓存·rust
鸟儿不吃草12 小时前
Android Java 自定义TextView点击取词,类似百度翻译的点击一段英文中的某个单词,可以显示点击了哪个单词
android·java·开发语言
梦梦代码精12 小时前
LikeShop 是怎么解决数据库瓶颈的?
java·数据库·低代码·php
eRRA OFAG12 小时前
mysql之联合索引
java
薪火铺子12 小时前
CAS单点登录原理与实践
java·后端
知兀12 小时前
【微服务/nacos】Nacos注册中心原理;配置服务发现中间、配置中心
java·微服务·架构
一 乐12 小时前
茶叶商城|基于springboot + vue茶叶商城系统(源码+数据库+文档)
java·数据库·vue.js·spring boot·论文·毕设·茶叶商城系统
AI进化营-智能译站12 小时前
ROS2 C++开发系列06:变量、数据类型与IO实战
java·开发语言·c++·ai