文章标题:
借助Redis与Caffeine构建高效二级缓存体系
文章内容:

各位好,我是摘星。今天要给大家介绍的是利用Redis和Caffeine来构建高性能的二级缓存,话不多说,直接进入主题~
目录
二级缓存架构的技术背景
1. 基础缓存架构
在现代分布式系统的设计里,缓存是优化服务性能的关键组件。标准的实现方式是把远程缓存(像Redis、Memcached这类)当作数据库的前置层,通过下面这些机制来提升性能:
– 读写策略:遵循Cache-Aside模式,只有当缓存中没有要找的数据时才去查询数据库
– 核心价值:
– 能把平均响应时间从数据库的10 – 100毫秒级别降到1 – 10毫秒
– 能让数据库负载降低50% – 80%(具体取决于命中率的变化)
2. 架构演进动因
当系统碰到下面这些场景时,纯远程缓存方案就会显出局限性:
| 问题类型 | 表现特征 | 典型案例 |
|—————-|————————|———————-|
| 超高并发读取 | Redis带宽成为瓶颈 | 热点商品详情页访问 |
| 超低延迟要求 | 网络往返耗时不可忽略 | 金融行情数据推送 |
| 成本控制需求 | 高频访问导致Redis扩容 | 用户基础信息查询 |
3. 二级缓存解决方案
引入本地缓存来构建两级缓存体系:
– 一级缓存:Caffeine(高性能本地缓存)
– 二级缓存:Redis Cluster(高可用远程缓存)
– 协同机制:
– 本地缓存设置短的TTL(秒级)
– 远程缓存设置长的TTL(分钟级)
– 通过PubSub来实现跨节点的失效
为何选用本地缓存?
1. 极速访问
内存级别的响应:本地缓存直接存放在应用进程的内存里(比如Java堆内),访问速度通常在纳秒级(像Caffeine的读写性能能达到每秒千万次),而远程缓存(比如Redis)需要网络通信,延迟在毫秒级。
| 技术选型 | 响应时长 |
|—————-|—————-|
| 本地缓存 | ~100ns
|
| Redis远程缓存 | ~1ms
(受网络影响可能更高) |
| 数据库查询 | ~10ms
甚至更长。 |
2. 减少网络IO
避免远程调用:每次访问Redis都得经过网络I/O(序列化、传输、反序列化),本地缓存完全跳过这个过程。
适用场景:高频访问的热点数据(比如商品详情、用户基础信息),通过本地缓存能减少90%以上的Redis请求。
3. 降低远程缓存和数据库压力
保护Redis:大量请求直接命中本地缓存,避免Redis成为瓶颈(尤其是在高并发场景下,比如秒杀、热点查询)。
减少穿透风险:本地缓存能设置短期的过期时间,避免缓存失效时大量请求直接冲击数据库。
4. 提升系统吞吐量
减少线程阻塞:远程缓存访问会阻塞线程(比如Redis的同步调用),本地缓存没有这个问题,特别适合高并发的服务。
案例:某电商系统引入Caffeine后,QPS从1万提升到5万,Redis负载下降60%。
5. 功能灵活
本地缓存支持丰富的特性,能满足不同业务需求:
– 淘汰策略:有LRU(最近最少使用)、LFU(最不经常使用)、FIFO等。
– 过期控制:支持基于时间(写入后过期、访问后过期)或者容量触发淘汰。
– 原子操作:比如get-if-absent-compute
(查不到时自动加载),避免并发重复查询。
本地内存具备的功能
1. 基本读写
功能:基础的键值存储和原子操作。
Cache<String, String> cache = Caffeine.newBuilder().build();
// 写入缓存
cache.put("user:1", "Alice");
// 读取缓存(若不存在则自动计算)
String value = cache.get("user:1", key -> fetchFromDB(key));
2. 缓存淘汰策略
功能:限制缓存大小并淘汰数据。
| 算法 | 描述 | 适用场景 | 代码示例(Caffeine) |
|——|————————–|———————-|——————————|
| LRU | 淘汰最久未访问的数据 | 热点数据分布不均匀 | .maximumSize(100).build()
|
| LFU | 淘汰访问频率最低的数据 | 长期稳定的热点数据 | .maximumSize(100).build()
(W-TinyLFU) |
| FIFO | 按写入顺序淘汰 | 数据顺序敏感的场景 | 需自定义实现 |
3. 过期时间控制
功能:自动清理过期数据。
Caffeine.newBuilder()
.expireAfterWrite(10, TimeUnit.MINUTES) // 写入后10分钟过期
.expireAfterAccess(5, TimeUnit.MINUTES) // 访问后5分钟过期
.build();

4. 缓存加载与刷新
功能:自动加载数据并支持后台刷新。
AsyncLoadingCache<String, String> cache = Caffeine.newBuilder()
.refreshAfterWrite(1, TimeUnit.MINUTES) // 1分钟后后台刷新
.buildAsync(key -> fetchFromDB(key));
// 获取数据(若需刷新,不会阻塞请求)
CompletableFuture<String> future = cache.get("user:1");
5. 并发控制
功能:线程安全与击穿保护。
// 自动合并并发请求(同一key仅一次加载)
LoadingCache<String, String> cache = Caffeine.newBuilder()
.build(key -> {
System.out.println("仅执行一次: " + key);
return fetchFromDB(key);
});
// 并发测试(输出1次日志)
IntStream.range(0, 100).parallel().forEach(
i -> cache.get("user:1")
);

6. 统计与监控
功能:记录命中率等指标。
Cache<String, String> cache = Caffeine.newBuilder()
.recordStats() // 开启统计
.build();
cache.get("user:1");
CacheStats stats = cache.stats();
System.out.println("命中率: " + stats.hitRate());
7. 持久化
功能:缓存数据持久化到磁盘。
// 使用Caffeine + RocksDB(需额外依赖)
Cache<String, byte[]> cache = Caffeine.newBuilder()
.maximumSize(100)
.writer(new CacheWriter<String, byte[]>() {
@Override public void write(String key, byte[] value) {
rocksDB.put(key.getBytes(), value); // 同步写入磁盘
}
@Override public void delete(String key, byte[] value, RemovalCause cause) {
rocksDB.delete(key.getBytes());
}
})
.build();
8. 事件监听
功能:监听缓存变更事件。
Cache<String, String> cache = Caffeine.newBuilder()
.removalListener((key, value, cause) ->
System.out.println("移除事件: " + key + " -> " + cause))
.evictionListener((key, value, cause) ->
System.out.println("驱逐事件: " + key + " -> " + cause))
.build();
本地缓存方案选型
1. ConcurrentHashMap
ConcurrentHashMap是Java集合框架里提供的线程安全哈希表实现,在JDK1.5中首次出现。它采用分段锁技术(JDK8后改成CAS + synchronized优化),通过把数据分成多个段(segment),每个段独立加锁,实现了高并发的读写能力。作为JUC(java.util.concurrent)包的核心组件,它被广泛应用在需要线程安全哈希表的场景中。
– 原生JDK支持,零外部依赖
– 读写性能接近非同步的HashMap
– 完全线程安全,支持高并发
– 提供原子性复合操作(如computeIfAbsent)
import java.util.concurrent.*;
import java.util.function.Function;
public class CHMCache<K,V> {
private final ConcurrentHashMap<K,V> map = new ConcurrentHashMap<>(16, 0.75f, 32);
private final ScheduledExecutorService cleaner = Executors.newSingleThreadScheduledExecutor();
// 基础操作
public void put(K key, V value) {
map.put(key, value);
}
// 带TTL的put
public void put(K key, V value, long ttl, TimeUnit unit) {
map.put(key, value);
cleaner.schedule(() -> map.remove(key), ttl, unit);
}
// 自动加载
public V get(K key, Function<K,V> loader) {
return map.computeIfAbsent(key, loader);
}
// 批量操作
public void putAll(Map<? extends K, ? extends V> m) {
map.putAll(m);
}
// 清空缓存
public void clear() {
map.clear();
}
}
2. Guava Cache
Guava Cache是Google Guava库中的缓存组件,诞生于2011年。作为ConcurrentHashMap的增强版,它添加了缓存特有的特性。Guava项目本身是Google内部Java开发的标准库,经过大规模生产环境验证,稳定性和性能都有保障。Guava Cache广泛应用在各种需要本地缓存的Java项目中。
– Google背书,质量有保证
– 丰富的缓存特性
– 良好的API设计
– 完善的文档和社区支持
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>31.1-jre</version>
</dependency>
import com.google.common.cache.*;
import java.util.concurrent.TimeUnit;
public class GuavaCacheDemo {
public static void main(String[] args) {
LoadingCache<String, String> cache = CacheBuilder.newBuilder()
.maximumSize(1000) // 最大条目数
.expireAfterWrite(10, TimeUnit.MINUTES) // 写入后过期时间
.expireAfterAccess(30, TimeUnit.MINUTES) // 访问后过期时间
.concurrencyLevel(8) // 并发级别
.recordStats() // 开启统计
.removalListener(notification ->
System.out.println("Removed: " + notification.getKey()))
.build(new CacheLoader<String, String>() {
@Override
public String load(String key) throws Exception {
return loadFromDB(key);
}
});
try {
// 自动加载
String value = cache.get("user:1001");
// 手动操作
cache.put("config:timeout", "5000");
cache.invalidate("user:1001");
// 打印统计
System.out.println(cache.stats());
} catch (ExecutionException e) {
e.printStackTrace();
}
}
private static String loadFromDB(String key) {
// 模拟数据库查询
return "DB_Result_" + key;
}
}
3. Caffeine
Caffeine是Guava Cache作者的新作品,发布于2015年。它专为现代Java应用设计,采用Window-TinyLFU淘汰算法,相比传统LRU有更高的命中率。Caffeine充分利用Java 8特性(如CompletableFuture),在性能上大幅超越Guava Cache(3 – 5倍提升),是目前性能最强的Java本地缓存库。
– 超高性能
– 更高的缓存命中率
– 异步刷新机制
– 精细的内存控制
<dependency>
<groupId>com.github.ben-manes.caffeine</groupId>
<artifactId>caffeine</artifactId>
<version>2.9.3</version>
</dependency>
import com.github.benmanes.caffeine.cache.*;
import java.util.concurrent.TimeUnit;
public class CaffeineDemo {
public static void main(String[] args) {
// 同步缓存
Cache<String, Data> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(5, TimeUnit.MINUTES)
.expireAfterAccess(10, TimeUnit.MINUTES)
.refreshAfterWrite(1, TimeUnit.MINUTES)
.recordStats()
.build();
// 异步加载缓存
AsyncLoadingCache<String, Data> asyncCache = Caffeine.newBuilder()
.maximumWeight(100_000)
.weigher((String key, Data data) -> data.size())
.expireAfterWrite(10, TimeUnit.MINUTES)
.buildAsync(key -> loadFromDB(key));
// 使用示例
Data data = cache.getIfPresent("key1");
CompletableFuture<Data> future = asyncCache.get("key1");
// 打印统计
System.out.println(cache.stats());
}
static class Data {
int size() { return 1; }
}
private static Data loadFromDB(String key) {
// 模拟数据库加载
return new Data();
}
}
4. Encache
EEhcache是Terracotta公司开发的企业级缓存框架,始于2003年。它是JSR-107标准实现之一,支持从本地缓存扩展到分布式缓存。Ehcache的特色在于支持多级存储(堆内/堆外/磁盘),适合需要缓存持久化的企业级应用。最新版本Ehcache 3.x完全重构,提供了更现代的API设计。
– 企业级功能支持
– 多级存储架构
– 完善的监控管理
– 良好的扩展性
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.9.7</version>
</dependency>
import org.ehcache.*;
import org.ehcache.config.*;
import org.ehcache.config.builders.*;
import java.time.Duration;
public class EhcacheDemo {
public static void main(String[] args) {
// 1. 配置缓存管理器
CacheManager cacheManager = CacheManagerBuilder.newCacheManagerBuilder()
.with(CacheManagerBuilder.persistence("/tmp/ehcache-data"))
.build();
cacheManager.init();
// 2. 配置缓存
CacheConfiguration<String, String> config = CacheConfigurationBuilder
.newCacheConfigurationBuilder(
String.class,
String.class,
ResourcePoolsBuilder.newResourcePoolsBuilder()
.heap(1000, EntryUnit.ENTRIES) // 堆内
.offheap(100, MemoryUnit.MB) // 堆外
.disk(1, MemoryUnit.GB, true) // 磁盘
)
.withExpiry(ExpiryPolicyBuilder.timeToLiveExpiration(Duration.ofMinutes(10)))
.build();
// 3. 创建缓存
Cache<String, String> cache = cacheManager.createCache("myCache", config);
// 4. 使用缓存
cache.put("key1", "value1");
String value = cache.get("key1");
System.out.println(value);
// 5. 关闭
cacheManager.close();
}
}
方案对比
特性 | ConcurrentHashMap | Guava Cache | Caffeine | Ehcache |
---|---|---|---|---|
基本缓存功能 | ✓ | ✓ | ✓ | ✓ |
过期策略 | ✗ | ✓ | ✓ | ✓ |
淘汰算法 | ✗ | LRU | W-TinyLFU | LRU/LFU |
自动加载 | ✗ | ✓ | ✓ | ✓ |
异步加载 | ✗ | ✗ | ✓ | ✗ |
持久化支持 | ✗ | ✗ | ✗ | ✓ |
多级存储 | ✗ | ✗ | ✗ | ✓ |