Java Caffeine 本地缓存

介绍

Caffeine 是一个基于 Java 8 开发的高性能本地缓存库,由 Ben Manes 开发,旨在作为 Google Guava Cache 的现代化替代品。它凭借极致的性能和超高的缓存命中率,被广泛应用于 Spring Boot、Hibernate 等主流框架中,被誉为 Java 本地缓存的首选方案。

🚀 核心优势:为何选择 Caffeine?

与 Guava Cache 或 ConcurrentHashMap 相比,Caffeine 在性能和功能上都有显著优势:

  • 极致的读写性能:通过高度优化的并发控制,其读写吞吐量远超同类框架,读操作性能接近原生的 ConcurrentHashMap。
  • 超高的缓存命中率:核心在于其先进的缓存淘汰算法,能有效识别并保留热点数据。
  • 丰富的功能特性:支持多种过期策略、异步加载、缓存统计等,功能完备且配置灵活。
  • 轻量级无依赖:核心包体积小,无任何第三方依赖,可轻松集成到项目中。

以下是 Caffeine 与 Guava Cache 的核心特性对比:

特性 Caffeine Guava Cache
淘汰算法 ✅ Window TinyLFU (命中率更高) LRU 近似
性能 ⚡ 极高 (接近理论最优) 良好
异步加载 ✅ AsyncLoadingCache ❌
社区活跃度 🌟 持续更新 🛑 维护模式

🧠 核心原理:W-TinyLFU 算法

Caffeine 高性能的灵魂在于其采用的 W-TinyLFU (Window TinyLFU) 缓存淘汰算法。该算法巧妙地结合了 LFU (Least Frequently Used) 和 LRU (Least Recently Used) 的优点,解决了传统算法的痛点:

  1. 解决 LRU 的“缓存污染”问题:LRU 仅关注数据的最近访问时间,当有大量低频数据突然涌入时,容易将真正的热点数据挤出缓存。
  2. 解决 LFU 的“数据僵化”问题:LFU 仅关注数据的访问频率,导致一些历史上高频但当前已不再访问的数据长期驻留缓存,无法被淘汰。

W-TinyLFU 通过“窗口缓存”对新数据进行频率预热,再结合频率过滤机制,既能快速接纳新的热点数据,又能有效剔除低频无效数据,从而在各种访问模式下都能保持极高的缓存命中率。

🛠️ 核心特性详解

Caffeine 提供了丰富且强大的功能,以满足不同业务场景的需求。

灵活的驱逐与过期策略

Caffeine 支持多种策略来自动管理缓存空间,防止内存溢出。

  • 基于容量驱逐:当缓存条目数量达到预设最大值时,自动淘汰“价值最低”的数据。
    .maximumSize(10_000) // 最多缓存 10,000 个条目
    
  • 基于权重驱逐:可以为每个缓存条目定义权重(如对象大小),当总权重超限时触发驱逐。
    .maximumWeight(100_000)
    .weigher((String key, Data value) -> value.sizeInBytes())
    
  • 基于时间过期:
    • expireAfterWrite: 写入后固定时间过期。
    • expireAfterAccess: 最后一次访问后固定时间过期。
    • expireAfter: 支持自定义每个条目的过期时间。

多样的缓存加载模式

  • 手动加载 (Cache):适用于需要显式 put/get 的场景。
    Cache<String, Object> cache = Caffeine.newBuilder().build();
    cache.put("key", "value");
    Object val = cache.getIfPresent("key");
    
  • 同步加载 (LoadingCache):当查询的键不存在时,自动调用加载函数来获取数据,避免缓存穿透。
    LoadingCache<String, String> cache = Caffeine.newBuilder()
    .build(key -> fetchDataFromDB(key));
    String value = cache.get("userId"); // 若缓存未命中,自动调用加载函数
    
  • 异步加载 (AsyncLoadingCache):基于 CompletableFuture 实现,在高并发场景下不阻塞调用线程,是性能最佳实践。
    AsyncLoadingCache<String, String> cache = Caffeine.newBuilder()
    .buildAsync(key -> CompletableFuture.supplyAsync(() -> fetchDataFromDB(key)));
    CompletableFuture<String> future = cache.get("userId");
    

其他实用功能

  • 缓存统计 (recordStats):开启后可以获取缓存命中率、加载时间、驱逐次数等关键指标,便于监控和调优。
  • 移除监听器 (removalListener):当数据因驱逐、过期或手动删除而被移除时,可以注册监听器接收通知。
  • 引用类型 (weakKeys/softValues):支持弱引用或软引用,当 JVM 内存不足时,GC 可以回收这些对象,有效防止内存泄漏。

使用

添加 Maven 依赖

<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>3.2.0</version> <!-- 请使用最新版本 -->
</dependency>

创建并使用缓存

import com.github.benmanes.caffeine.cache.Cache;
import java.util.concurrent.TimeUnit;

public class CaffeineExample {
        public static void main(String[] args) {
            // 构建一个缓存
            Cache<String, String> cache = Caffeine.newBuilder()
                .maximumSize(100)
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .recordStats()
                .build();

            // 存入数据
            cache.put("key1", "value1");

            // 获取数据
            String value = cache.getIfPresent("key1");
            System.out.println(value); // 输出: value1

            // 获取或计算 (如果不存在则执行函数并放入缓存)
            String value2 = cache.get("key2", k -> "computed-" + k);
            System.out.println(value2); // 输出: computed-key2

            // 查看统计信息
            System.out.println(cache.stats());
        }
}