返回文章列表

文章

Java中的Cache库

Caffeine、Ehcache、JCache(JSR-107)、Guava Cache、JetCache 等的综合对比

目录
  1. 1. Caffeine(/ˈkæfiːn/)
  2. 2. Ehcache
  3. 3. JCache(JSR-107)
  4. 5. JetCache
  5. 总结对比表格
  6. 📎 参考文章

Java 领域有众多优秀的缓存库,各有侧重。下面从架构模式、功能特性、性能表现、扩展性及典型应用场景和实例代码几个维度对 Caffeine、Ehcache、JCache(JSR-107)、Guava Cache、JetCache 进行对比分析。

1. Caffeine(/ˈkæfiːn/)#

架构模式:

  • 本地缓存 (In-Process Cache): 纯内存缓存,数据存储在应用程序的 JVM 堆中。
  • 高性能设计: 借鉴了 ConcurrentHashMap 的设计思想,并引入了 Window TinyLFU 淘汰算法,优化了并发读写性能和缓存命中率。 功能特性:
  • 丰富的淘汰策略: 支持基于容量、时间(写入后过期 expireAfterWrite、访问后过期 expireAfterAccess)以及自定义策略(weigher)的淘汰。
  • 异步操作: 支持异步加载、异步刷新和异步淘汰,提高性能,避免阻塞。
  • 统计功能: 提供缓存命中率、加载成功/失败次数、淘汰数量等详细统计信息。
  • 引用类型支持: 支持软引用和弱引用(但通常不推荐用于高性能缓存,因其依赖 GC)。
  • 与 Spring Cache 集成: 完美支持 Spring Framework 的缓存抽象。 性能表现:
  • 业界公认的高性能: 通常被认为是目前 Java 本地缓存中性能最好的。在并发读写和高吞吐量场景下表现卓越。
  • 低延迟: 数据直接在内存中,避免了网络开销,因此读写延迟极低。 扩展性:
  • 易于集成: 提供了简洁的 API,易于与现有项目集成。
  • 可自定义: 允许自定义缓存加载器 CacheLoader 和缓存移除监听器 RemovalListener
  • 不具备分布式能力: 本身是本地缓存,不提供跨 JVM 的分布式缓存能力。如需分布式,需结合其他技术(如消息队列)。 典型应用场景:
  • 单体应用中需要高性能、低延迟的本地数据缓存。
  • 读多写少的场景,例如配置信息、字典数据、热门商品列表等。
  • Spring Boot 应用中作为默认的本地缓存实现。 使用实例:
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;

import java.util.concurrent.TimeUnit;

public class CaffeineExample {
    public static void main(String[] args) {
        // 创建一个基于容量的缓存,最大容量为10000,并在写入5分钟后过期
        Cache<String, String> cache = Caffeine.newBuilder()
                .maximumSize(10000)
                .expireAfterWrite(5, TimeUnit.MINUTES)
                .build();

        // 存入缓存
        cache.put("key1", "value1");
        cache.put("key2", "value2");

        // 从缓存获取
        String value1 = cache.getIfPresent("key1");
        System.out.println("Value for key1: " + value1); // Output: Value for key1: value1

        // 如果不存在则计算并放入缓存
        String value3 = cache.get("key3", k -> {
            System.out.println("Calculating value for key3...");
            return "calculatedValue3";
        });
        System.out.println("Value for key3: " + value3); // Output: Value for key3: calculatedValue3

        // 移除缓存项
        cache.invalidate("key2");
        System.out.println("Value for key2 after invalidation: " + cache.getIfPresent("key2")); // Output: Value for key2 after invalidation: null
    }
}

2. Ehcache#

架构模式:

  • 多层缓存: 支持内存(堆内、堆外)和磁盘存储,可以配置为多层缓存结构。
  • 可嵌入式: 可以在应用程序内部运行,也可以作为独立进程运行。 功能特性:
  • 丰富的数据存储选项: 支持堆内、堆外(Off-heap)和磁盘存储,允许管理更大容量的缓存。
  • 多种淘汰策略: 支持 LRU、LFU、FIFO 以及自定义淘汰策略。
  • 持久化: 支持将缓存数据持久化到磁盘,应用程序重启后数据不会丢失。
  • 集群和分布式功能: 在 Ehcache 2.x 版本中支持分布式缓存(通过 Terracotta Ehcache Server 或 RMI),但 Ehcache 3.x 更加侧重于本地缓存,其分布式能力通常需要与外部分布式方案(如 Hazelcast、Ignite)结合。
  • 监听器: 支持缓存事件监听。 性能表现:
  • 性能良好: 作为老牌缓存库,性能表现稳定,但相对于 Caffeine 在纯内存场景下通常略逊一筹。
  • I/O 消耗: 当使用堆外或磁盘存储时,会涉及 I/O 操作,性能会低于纯内存缓存。 扩展性:
  • 功能全面: 作为老牌缓存库,功能非常丰富,包括集群、持久化等。
  • 配置复杂: 相较于 Caffeine 和 Guava Cache,Ehcache 的配置通常更为复杂,尤其是涉及到多层存储和集群时。
  • 社区活跃度下降: 相对于 Caffeine,其在本地缓存领域的活跃度有所下降,但在企业级应用中仍有大量使用。 典型应用场景:
  • 需要持久化缓存数据的应用。
  • 需要管理较大缓存容量,超出 JVM 堆限制的场景(通过堆外或磁盘存储)。
  • 传统 Java EE 应用,以及需要将缓存分层管理的场景。 使用实例(Ehcache 3.x):
import org.ehcache.Cache;
import org.ehcache.CacheManager;
import org.ehcache.config.builders.CacheConfigurationBuilder;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.config.builders.ResourcePoolsBuilder;
import org.ehcache.config.units.EntryUnit;
import org.ehcache.config.units.MemoryUnit;

public class EhcacheExample {
    public static void main(String[] args) {
        // 创建CacheManager
        CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
                .withCache("preConfigured",
                        CacheConfigurationBuilder.newCacheConfigurationBuilder(String.class, String.class,
                                ResourcePoolsBuilder.newResourcePoolsBuilder()
                                        .heap(10, EntryUnit.ENTRIES) // 堆内最大10个条目
                                        .offheap(1, MemoryUnit.MB) // 堆外1MB
                                        .disk(20, MemoryUnit.MB, true) // 磁盘20MB,持久化
                        ).build())
                .build(true); // true表示初始化并启动CacheManager

        // 获取缓存实例
        Cache<String, String> myCache = cacheManager.getCache("preConfigured", String.class, String.class);

        // 存入缓存
        myCache.put("key1", "value1");
        myCache.put("key2", "value2");

        // 从缓存获取
        String value1 = myCache.get("key1");
        System.out.println("Value for key1: " + value1); // Output: Value for key1: value1

        // 移除缓存项
        myCache.remove("key2");
        System.out.println("Value for key2 after removal: " + myCache.get("key2")); // Output: Value for key2 after removal: null

        // 关闭CacheManager
        cacheManager.close();
    }
}

3. JCache(JSR-107)#

架构模式:

  • 缓存抽象 API: JCache (JSR-107) 是 Java 平台上的一个缓存 API 规范,而不是具体的缓存实现。它定义了缓存的通用接口和行为,允许开发者使用统一的 API 来操作不同的缓存实现。
  • SPI(Service Provider Interface): 具体的缓存实现(如 Caffeine、Ehcache)需要遵循 JCache 规范,提供相应的 SPI 实现,才能被 JCache 统一管理。 功能特性:
  • 标准化 API: 提供了一套标准的缓存 API,使得应用程序与底层缓存实现解耦。
  • CRUD 操作: 定义了基本的缓存存取、更新、删除操作。
  • 过期策略: 定义了时间(创建后过期、访问后过期、更新后过期)。
  • 事件监听: 支持缓存事件的监听。
  • 事务支持: 提供对缓存事务的支持。
  • 统计: 定义了缓存统计信息的接口。 性能表现:
  • 不直接决定性能: JCache 本身不提供缓存实现,其性能完全取决于底层实际使用的缓存提供商(如 Caffeine、Ehcache)。
  • 可能引入少量开销: 由于一层抽象,可能会引入微小的性能开销,但在实际应用中通常可以忽略。 扩展性:
  • 高度可插拔: 最大的优势是允许在不修改业务代码的情况下切换不同的缓存实现。
  • 生态系统: 许多主流的缓存库都提供了 JCache 实现,例如 Caffeine、Ehcache、Hazelcast、Infinispan 等。 典型应用场景:
  • 需要与具体缓存实现解耦的应用程序。
  • 在不同的部署环境或项目阶段可能需要更换缓存实现。
  • 使用 Spring Cache 等缓存抽象框架时,JCache 可以作为其底层实现之一。 使用实例 (结合 Caffeine 作为 JCache 实现): 首先需要添加 javax.cache:cache-api 和 Caffeine 的 JCache adapter 依赖。
<dependency>
    <groupId>javax.cache</groupId>
    <artifactId>cache-api</artifactId>
    <version>1.1.1</version>
</dependency>
<dependency>
    <groupId>com.github.benmanes.caffeine</groupId>
    <artifactId>jcache</artifactId>
    <version>3.1.8</version>
</dependency>