Caffeine

Caffeine 是基于 Java 8 的高性能进程内缓存库,是 Spring Boot 2.x+ 默认的本地缓存实现。相比 Guava Cache,引入了 W-TinyLFU 淘汰算法,命中率显著更高。

适合:单机高频读场景(热点数据、配置缓存);不适合:多节点共享数据(用 Redis)。


快速上手

<dependency>
    <groupId>com.github.ben-manes.caffeine</groupId>
    <artifactId>caffeine</artifactId>
    <version>3.1.8</version>
</dependency>

手动缓存(Cache)

Cache<String, User> cache = Caffeine.newBuilder()
    .maximumSize(1000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .recordStats()
    .build();
 
cache.put("user:1", user);
User u = cache.getIfPresent("user:1");                        // 不存在返回 null
User u = cache.get("user:1", k -> userRepo.findById(1L));    // 不存在则加载
cache.invalidate("user:1");
cache.invalidateAll();

自动加载缓存(LoadingCache)

LoadingCache<String, User> cache = Caffeine.newBuilder()
    .maximumSize(1000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .build(key -> userRepo.findByKey(key));   // 缺失时自动调用
 
User user = cache.get("user:1");
Map<String, User> users = cache.getAll(List.of("user:1", "user:2"));

异步加载缓存(AsyncLoadingCache)

AsyncLoadingCache<String, User> cache = Caffeine.newBuilder()
    .maximumSize(1000)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .buildAsync(key -> userRepo.findByKeyAsync(key));  // 返回 CompletableFuture
 
CompletableFuture<User> future = cache.get("user:1");

过期策略

策略方法说明
写后过期expireAfterWrite(duration)写入后固定时间过期
访问后过期expireAfterAccess(duration)最后一次读/写后固定时间过期
自定义过期expireAfter(Expiry)按 key/value 动态计算过期时间
固定大小maximumSize(n)超出时按 W-TinyLFU 淘汰
权重上限maximumWeight(w)按条目权重总和限制

统计监控

CacheStats stats = cache.stats();
stats.hitRate();          // 命中率
stats.loadCount();        // 加载次数
stats.evictionCount();    // 驱逐次数
stats.averageLoadPenalty(); // 平均加载耗时(ns)

Spring Boot 集成

spring:
  cache:
    type: caffeine
    caffeine:
      spec: maximumSize=500,expireAfterWrite=600s
@Configuration
@EnableCaching
public class CacheConfig {
    @Bean
    public CacheManager cacheManager() {
        CaffeineCacheManager manager = new CaffeineCacheManager();
        manager.setCaffeine(Caffeine.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .recordStats());
        return manager;
    }
}
@Cacheable(value = "users", key = "#id")
public User getUser(Long id) { ... }
 
@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) { ... }
 
@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) { ... }

W-TinyLFU 算法

全部缓存空间
  ├── Window 区(1%):新进入数据先放这里,防止冷数据污染
  └── Main 区(99%)
       ├── Protected 区(80%):高频访问数据
       └── Probation 区(20%):候选区,频率低的在此被淘汰

新数据进 Window → 晋升 Probation → 高频则升入 Protected,否则被淘汰。对突发流量和循环扫描的抵抗性优于纯 LRU。


Caffeine vs Guava Cache

维度CaffeineGuava Cache
淘汰算法W-TinyLFULRU 近似
命中率更高较低
异步加载支持不支持
Spring Boot 默认是(2.x+)

相关链接

  • Redis — 分布式缓存,多节点共享数据的首选
  • Druid — 数据库连接池,常与本地缓存配合减少数据库压力