快速上手 Caffeine:Java 缓存库初学者指南
一、背景
- 简介: Caffeine 是一个高性能的 Java 缓存库,旨在为现代应用程序提供快速、高效的缓存解决方案。它由 Google Guava Cache 的创始人之一开发,具备基于时间的过期、基于大小的回收、异步加载、统计信息等多种特性。Caffeine的性能有多么强大呢?以下是官方给出的基准测试图(在该基准测试中,8 个线程同时从配置了最大容量的高速缓存中读取数据。)
-
应用场景:
- Web 应用的会话管理:缓存用户会话信息,以减少数据库查询次数。
- 配置和数据的本地缓存:缓存配置文件或常用数据,以减少对外部系统的访问延迟。
- 计算结果缓存:缓存频繁计算的结果,如搜索结果、数据分析结果等,以提高响应速度。
- 分布式系统中的中间缓存层:在分布式系统中使用本地缓存,减轻数据库或其他后端服务的负载。
-
示例代码:
- 导入依赖:
<dependency> <groupId>com.github.ben-manes.caffeine</groupId> <artifactId>caffeine</artifactId> </dependency>
- 测试代码:
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) { // 创建一个Caffeine缓存 Cache<String, String> cache = Caffeine.newBuilder() .maximumSize(100) // 设置缓存的最大条目数 .expireAfterWrite(10, TimeUnit.MINUTES) // 设置写入后10分钟过期 .build(); // 放入一个条目 cache.put("key1", "value1"); // 获取一个条目 String value = cache.getIfPresent("key1"); System.out.println("key1: " + value); // 使用计算函数获取一个条目(如果不存在,则计算并放入缓存) value = cache.get("key2", k -> "computedValue"); System.out.println("key2: " + value); // 删除一个条目 cache.invalidate("key1"); } }
二 、Caffeine 常见缓存淘汰策略
缓存淘汰策略用于在缓存空间有限时,确保频繁访问和最近使用的条目得以保留,同时移除不常用的条目,从而优化内存使用和缓存命中率。
-
基于容量的清理:
Caffeine
允许配置缓存的最大容量,当缓存容量达到上限时,Caffeine
会根据其内部的策略自动清理一些缓存项。- 这种策略通常采用窗口TinyLFU算法(Least Frequently Used)来判断条目的使用频率。
// 创建一个最大容量为3的缓存
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(3)
.build();
-
基于时间的清理:
- TTL(Time to Live):在缓存项创建的时指定一段时间,到期进行自动清理。
- TTR(Time to Refresh):在缓存项最后一次访问一段时间后进行自动清理。
// 创建一个写入后2秒过期的缓存
Cache<String, String> cache = Caffeine.newBuilder()
.expireAfterWrite(2, TimeUnit.SECONDS)
.build();
// 创建一个访问后2秒过期的缓存
Cache<String, String> cache = Caffeine.newBuilder()
.expireAfterAccess(2, TimeUnit.SECONDS)
.build();
- 显式移除:可以通过API手动移除缓存中的条目。
// 创建一个缓存
Cache<String, String> cache = Caffeine.newBuilder()
.maximumSize(100)
.build();
// 放入一个条目
cache.put("key1", "value1");
// 缓存应该包含条目
System.out.println("Initial Cache: " + cache.getIfPresent("key1"));
// 显式移除条目
cache.invalidate("key1");
// 缓存应该不包含条目
System.out.println("After Removal: " + cache.getIfPresent("key1"));
三、常见API
-
创建缓存:
Caffeine.newBuilder()
: 创建一个新的缓存构建器,可以设置各种缓存参数。
-
缓存配置:
maximumSize(long size)
: 设置缓存的最大容量,超过这个容量后会触发缓存回收。expireAfterWrite(long duration, TimeUnit unit)
: 设置缓存项在写入后多长时间过期。
expireAfterAccess(long duration, TimeUnit unit)
: 设置缓存项在访问后多长时间过期。
-
缓存操作:
put(K key, V value)
: 将一个键值对放入缓存。
getIfPresent(Object key)
: 获取缓存中指定键的值,如果不存在则返回null
。
get(K key, Function<? super K, ? extends V> mappingFunction)
: 获取缓存中指定键的值,如果不存在则使用提供的映射函数计算值并放入缓存。
invalidate(Object key)
: 移除缓存中指定键的条目。
invalidateAll(Iterable<?> keys)
: 移除缓存中指定键集合的所有条目。
invalidateAll()
: 移除缓存中的所有条目。
四、Redis 与 Caffeine
1. 适用 Caffeine 的场景
- 本地缓存需求:适合数据量小且仅需本地缓存的场景,如单节点应用或无需共享缓存的多节点应用。
- 高性能和低延迟要求:适用于需要极低访问延迟和高吞吐量的应用,因为Caffeine运行在本地内存中,访问速度快。
- 简化架构:无需引入额外组件,只需在应用中引入相应库即可,简化系统架构。
- 不需要持久化:适用于无需持久化的缓存数据,如缓存计算结果或短时间内频繁访问的数据。
2. 适用 Redis 的场景
- 分布式缓存需求:适合多个应用服务器共享缓存,作为独立的分布式缓存系统,支持多个客户端访问。
- 大数据量缓存:适用于缓存数据量超出单个服务器内存限制的场景,通过分布式特性将数据分布到多个节点。
- 持久化需求:需要持久化缓存数据以防数据丢失,提供数据持久化功能确保数据可靠性。
- 丰富的数据结构:支持多种数据结构,如字符串、哈希、列表、集合和有序集合,适用于复杂缓存场景。
3. 综合比较
- 访问速度:Caffeine 本地缓存访问速度更快,但 Redis 可通过分片和集群提高读写性能。
- 数据共享:Redis 适合需要多个应用实例共享数据的场景,而 Caffeine 仅适用于单实例缓存。
- 数据持久化:Redis 提供持久化机制,适合需要持久保存缓存数据的应用。Caffeine 不支持持久化。
- 运维复杂度:Caffeine 易于使用和集成,无需额外运维工作。Redis 需要独立部署和运维。
转载自:https://juejin.cn/post/7392105060899340299