Caffeine缓存入门-理论篇

23 阅读4分钟

Caffeine缓存框架简介

Caffeine是一个高性能的Java缓存库,它基于Java 8及以上版本构建,提供了丰富的特性和灵活的配置选项,以支持各种复杂的缓存需求。在本教程中,我们将详细介绍Caffeine的使用方法和最佳实践,帮助您在实际项目中高效地使用Caffeine。

为什么选择Caffeine?

选择Caffeine作为缓存解决方案的理由有很多,以下是一些关键点:

  1. 强大的过期策略

    • 与谷歌提供的ConcurrentLinkedHashMap相比,Caffeine提供了多种基于时间的淘汰机制。这意味着您可以根据实际需求,设置缓存数据的自动过期时间,无论是基于数据的访问时间、写入时间还是自定义的时间规则。
  2. 高命中率

    • Caffeine采用了Window TinyLfu( Least Frequently Used)算法来自动调整缓存的大小,以实现接近最佳的命中率。这一特性使得Caffeine在许多场景下比传统的Guava Cache更加高效。
  3. Spring框架推荐

    • 从Spring Framework 5.0(Spring Boot 2.0)开始,Spring官方推荐使用Caffeine作为缓存解决方案,取代了之前的Guava Cache。

Caffeine的主要特点

Caffeine的以下特点使其成为现代Java应用的理想缓存库:

  • 基于Java 8的高性能缓存库:Caffeine利用Java 8的新特性,如Lambda表达式和Stream API,提供了高性能的缓存操作。

  • 灵活的缓存构建:Caffeine提供了多种灵活的构造方法,允许开发者创建具有不同特性的本地缓存,例如自动加载数据、基于数量和失效时间的剔除策略、异步刷新等。

  • 丰富的特性

    • 数据剔除提醒:Caffeine允许您在数据被淘汰时接收通知,这使得您可以在数据被移除前执行一些操作,如数据持久化。
    • 写入广播机制:Caffeine支持在数据写入时触发事件,这有助于实现复杂的业务逻辑。
    • 缓存访问统计:Caffeine提供了缓存访问的统计信息,帮助您监控和优化缓存性能。

通过本教程,您将学习如何利用Caffeine的这些特性,构建高效、可扩展的缓存解决方案,以满足您的业务需求。

缓存策略

Caffeine支持多种缓存策略,以下是一些主要的策略:

最大容量(Maximum Size)

限制缓存中可以存储的最大条目数。当达到这个上限时,缓存会根据其淘汰算法自动移除一些条目。

Cache<String, String> cache = Caffeine.newBuilder()
    .maximumSize(100)
    .build();

基于时间的过期策略

访问后过期(Expire After Access)

指定一个时间间隔,在该时间内如果没有访问某个缓存项,则认为它已过期,并在下次尝试访问时将其移除。

Cache<String, String> cache = Caffeine.newBuilder()
    .expireAfterAccess(10, TimeUnit.MINUTES)
    .build();

写入后过期(Expire After Write)

指定一个时间间隔,从写入(创建或更新)缓存项开始计时,超过该时间间隔后认为它已过期。

Cache<String, String> cache = Caffeine.newBuilder()
    .expireAfterWrite(5, TimeUnit.HOURS)
    .build();

基于频率的淘汰策略

最近最少使用(LRU)

保留最近使用的条目,而移除最久未使用的条目。

最不经常使用(LFU)

保留最常使用的条目,而移除使用频率最低的条目。

Cache<String, String> cache = Caffeine.newBuilder()
    .maximumSize(100)
    .expireAfterAccess(10, TimeUnit.MINUTES)
    .build();

自动刷新(Refresh After Write)

允许你定义一个时间间隔,当超过这个时间间隔时,即使缓存项没有过期,也会尝试重新加载数据。

LoadingCache<String, String> loadingCache = Caffeine.newBuilder()
    .refreshAfterWrite(1, TimeUnit.HOURS)
    .build(key -> fetchDataFromDatabase(key));

权重(Weigher)

允许每个缓存条目具有不同的权重,这对于存储大小各异的对象特别有用。

Cache<String, byte[]> cache = Caffeine.newBuilder()
    .maximumWeight(1_000_000)
    .weigher((Weigher<String, byte[]>) (key, value) -> value.length)
    .build();

加载器

Caffeine提供了同步和异步两种加载器,用于在缓存中没有找到指定的键时自动加载数据。

同步加载器(Synchronous Loader)

LoadingCache<String, String> cache = Caffeine.newBuilder()
    .build(new CacheLoader<String, String>() {
        @Override
        public String load(String key) throws Exception {
            return fetchDataFromDatabase(key);
        }
    });

异步加载器(Asynchronous Loader)

AsyncLoadingCache<String, String> asyncCache = Caffeine.newBuilder()
    .buildAsync(new AsyncCacheLoader<String, String>() {
        @Override
        public CompletableFuture<String> asyncLoad(String key, Executor executor) {
            return CompletableFuture.supplyAsync(() -> fetchDataFromDatabase(key), executor);
        }
    });

异步缓存(Asynchronous Cache)

异步缓存允许以非阻塞的方式从缓存中获取数据。

AsyncLoadingCache<String, String> asyncCache = Caffeine.newBuilder()
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .buildAsync((key, executor) -> CompletableFuture.supplyAsync(() -> fetchDataFromDatabase(key), executor));

刷新策略(Refresh Policy)

刷新策略用于在缓存项仍然有效的情况下主动更新其值。

LoadingCache<String, String> cache = Caffeine.newBuilder()
    .refreshAfterWrite(1, TimeUnit.HOURS)
    .build(new CacheLoader<String, String>() {
        @Override
        public String load(String key) throws Exception {
            return fetchDataFromDatabase(key);
        }
    });
​

统计信息(Statistics)

Caffeine允许你收集缓存操作的统计信息。

LoadingCache<String, String> cache = Caffeine.newBuilder()
    .maximumSize(100)
    .expireAfterWrite(10, TimeUnit.MINUTES)
    .recordStats()
    .build(key -> fetchDataFromDatabase(key));
​
CacheStats stats = cache.stats();
System.out.println("Hit count: " + stats.hitCount());

回收(Eviction)

回收是指当缓存达到其容量限制或其他条件触发时,自动移除一些条目的过程。

Cache<String, String> cache = Caffeine.newBuilder()
    .maximumSize(100)
    .build();