维度 | Redis(分布式) | Caffeine(本地) |
---|---|---|
数据存储位置 | 独立内存服务器 | 应用进程堆内存 |
数据一致性 | 强一致(集群版) | 最终一致(需额外同步) |
网络开销 | 存在TCP/IP通信 | 无网络延迟 |
数据容量 | 支持TB级存储 | 受限于JVM堆大小 |
数据结构 | 支持5种核心数据结构 | 仅Key-Value结构 |
持久化能力 | RDB/AOF | 需结合其他存储 |
yaml
# 测试硬件
- CPU: Intel i7-12700H (14核20线程)
- 内存: 32GB DDR5
- 网络: 本地千兆环回测试
# 软件版本
- Spring Boot 3.2.4
- Redis 7.2.4(单节点)
- Caffeine 3.1.8
- JMH 1.37
java
@State(Scope.Thread)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
public class CacheBenchmark {
private CacheService redisCache;
private CacheService caffeineCache;
private final String key = "testKey";
private final String value = "testValue".repeat(1000);
@Setup
public void setup() {
redisCache = new RedisCacheService(); // 连接池配置最大50连接
caffeineCache = new CaffeineCacheService(100_000); // 最大容量10万
}
@Benchmark
public void redisPut() {
redisCache.put(key, value);
}
@Benchmark
public String redisGet() {
return redisCache.get(key);
}
@Benchmark
public void caffeinePut() {
caffeineCache.put(key, value);
}
@Benchmark
public String caffeineGet() {
return caffeineCache.get(key);
}
}
terminal
# 写入性能
Benchmark Mode Cnt Score Error Units
CacheBenchmark.redisPut thrpt 10 4523.467 ± 234.12 ops/s
CacheBenchmark.caffeinePut thrpt 10 1245678.91 ± 4567.89 ops/s
# 读取性能
CacheBenchmark.redisGet thrpt 10 6821.354 ± 312.45 ops/s
CacheBenchmark.caffeineGet thrpt 10 2147586.33 ± 6789.12 ops/s
terminal
Redis PUT: 2.34ms
Redis GET: 1.78ms
Caffeine PUT: 0.023μs
Caffeine GET: 0.015μs
数据量 | Redis内存占用 | Caffeine堆内存 |
---|---|---|
10万条1KB数据 | 约120MB | 约210MB |
100万条1KB数据 | 约1.2GB | OOM(默认堆1G) |
java
@Bean
public LettuceConnectionFactory redisConnectionFactory() {
LettuceClientConfiguration config = LettuceClientConfiguration.builder()
.commandTimeout(Duration.ofMillis(500))
.clientOptions(ClientOptions.builder()
.autoReconnect(true)
.publishOnScheduler(true)
.build())
.clientResources(DefaultClientResources.builder()
.ioThreadPoolSize(8)
.computationThreadPoolSize(4)
.build())
.build();
return new LettuceConnectionFactory(
new RedisStandaloneConfiguration("localhost", 6379),
config
);
}
java
@Bean
public CacheManager caffeineCacheManager() {
return new CaffeineCacheManager() {
@Override
protected Cache
mermaid
sequenceDiagram
participant Client
participant L1Cache as Caffeine(L1)
participant L2Cache as Redis(L2)
participant DB
Client->>L1Cache: 读请求
alt L1命中
L1Cache-->>Client: 返回数据
else L1未命中
L1Cache->>L2Cache: 读请求
alt L2命中
L2Cache-->>L1Cache: 返回数据
L1Cache-->>Client: 返回数据
else L2未命中
L2Cache->>DB: 查询数据
DB-->>L2Cache: 返回数据
L2Cache-->>L1Cache: 写入数据
L1Cache-->>Client: 返回数据
end
end
java
@Cacheable(value = "users", cacheManager = "compositeCacheManager")
public User getUser(Long id) {
// 先查本地缓存,再查Redis,最后查DB
}
@CacheEvict(value = "users", allEntries = true)
public void updateUser(User user) {
// 更新时双删策略
redisTemplate.delete("users::" + user.getId());
caffeineCache.invalidate(user.getId());
}
java
@Bean
public RedisTemplate redisTemplate() {
RedisTemplate template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory());
// 使用Protostuff序列化
ProtostuffRedisSerializer serializer = new ProtostuffRedisSerializer();
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(serializer);
template.setHashKeySerializer(new StringRedisSerializer());
template.setHashValueSerializer(serializer);
return template;
}
java
// 启用ZGC垃圾回收器
JAVA_OPTS="-XX:+UseZGC -Xms4g -Xmx4g"
// 使用Offheap存储(需配合Ohc模块)
Cache cache = Caffeine.newBuilder()
.maximumSize(100_000)
.executor(MoreExecutors.directExecutor())
.evictionListener((key, value, cause) -> {/* 监听驱逐事件 */})
.weigher((key, value) -> ((byte[])value).length)
.build();
指标 | Redis优势场景 | Caffeine优势场景 |
---|---|---|
吞吐量 | 万级QPS | 百万级QPS |
延迟 | 毫秒级 | 微秒级 |
数据一致性 | 强一致 | 最终一致 |
扩展性 | 水平扩展 | 垂直扩展 |
成本 | 需要独立服务器 | 零额外成本 |
黄金法则:
任务1:设计混合缓存监控面板
java
// Caffeine统计
CacheStats stats = caffeineCache.stats();
// 命中率、加载时间、驱逐数量等指标
// Redis统计
Info info = redisTemplate.getRequiredConnectionFactory()
.getConnection().info("stats");
// 连接数、命令统计、内存用量等
任务2:实现缓存雪崩防护
java
// 随机过期时间
.expireAfterWrite(10 + ThreadLocalRandom.current().nextInt(5), TimeUnit.MINUTES)
// 互斥锁重建
public User getWithMutex(Long id) {
String key = "user:" + id;
User value = cache.get(key);
if (value == null) {
String lockKey = "lock:" + key;
if (redisLock.tryLock(lockKey)) {
try {
// 查库重建缓存
} finally {
redisLock.unlock(lockKey);
}
} else {
Thread.sleep(50);
return cache.get(key);
}
}
return value;
}
如何设计缓存分片策略提升Redis性能?
java
Config config = new Config();
config.useClusterServers()
.addNodeAddress("redis://127.0.0.1:7000", "redis://127.0.0.1:7001")
.setScanInterval(2000);
RedissonClient client = Redisson.create(config);
本地缓存如何实现分布式同步?
java
@EventListener
public void handleCacheEvictEvent(CacheEvictEvent event) {
caffeineCache.invalidate(event.getKey());
}
通过本文你将掌握:
✅ 缓存技术选型方法论
✅ 性能基准测试实践技巧
✅ 生产级缓存配置方案
✅ 混合缓存架构设计思路
讨论话题:
在云原生环境下如何设计弹性缓存架构?如何平衡缓存成本与性能收益?