跳到主要内容
Java 工业级多级缓存架构设计与落地(Redis 客户端+Redisson 方案) | 极客日志
Java java
Java 工业级多级缓存架构设计与落地(Redis 客户端+Redisson 方案) 综述由AI生成 介绍 Java 工业级多级缓存架构设计与落地方案。涵盖本地缓存 Caffeine 配置与封装,分布式缓存 Redis 原生客户端(Lettuce/Jedis)及 Redisson 实现。详细对比了各方案性能、开发效率与适用场景,提供高并发首选(Caffeine+Redis+Lettuce)与快速开发首选(Caffeine+Redisson)的全链路整合代码。重点解决缓存穿透、击穿、雪崩三大问题,包含空值缓存、分布式锁、延迟队列等实战策略,并给出选型决策流程与扩展建议。
PgDevote 发布于 2026/3/26 更新于 2026/5/23 29 浏览前言
在高并发 Java 应用中,缓存是提升系统性能的核心技术之一——它通过'以空间换时间'的逻辑,将热点数据临时存储在内存中,大幅减少数据库 IO 开销,让接口响应时间从毫秒级降至微秒级。不同于 MyBatis 一级/二级缓存的'ORM 层局部缓存'定位,本文聚焦的是'业务级全局缓存',覆盖从单机到分布式、从基础实现到高级优化的全链路方案。
本文作为系列开篇,核心目标是分方案讲透实现,按场景明确选型 :先分别落地'Redis 原生客户端(Lettuce/Jedis)'和'Redisson 分布式工具'两大技术路线,再通过深度对比明确不同场景的最优选择,最终给出工业级融合落地实践。无论你是需要解决中小并发的单体应用,还是支撑百万级 QPS 的分布式集群,都能从本文找到可直接复用的方案。
系列衔接说明:本文聚焦多级缓存的基础架构与核心实现,解决缓存穿透、击穿、雪崩三大经典问题;下一篇将基于本文方案,补充布隆过滤器作为缓存穿透的终极解决方案,形成'基础缓存 + 高级防护'的完整技术闭环。
一、缓存核心认知与架构设计
1.1 缓存核心分类与价值
缓存本质是'数据的临时存储介质',按存储位置可分为两大类,核心差异直接决定了选型逻辑:
缓存类型 典型实现 核心优势 核心局限 核心价值 本地缓存(JVM 级) Caffeine、Guava Cache、ConcurrentHashMap 无网络开销,查询性能极致(微秒级) 跨服务数据不一致,受 JVM 内存限制 本地热点数据加速,减少分布式缓存访问压力 分布式缓存(跨应用) Redis、Memcached 全局数据一致,容量可横向扩展 存在网络 IO 开销(毫秒级) 跨服务数据共享,支撑分布式架构下的缓存需求
多级缓存架构的核心逻辑 :将两者结合,形成'本地缓存优先查询,分布式缓存兜底同步'的链路——既利用本地缓存的高性能,又通过分布式缓存保证跨服务一致性,是工业级应用的标配架构。
1.2 缓存三大核心问题(定义 + 业务影响)
缓存架构设计的核心是'利用优势,规避风险',三大经典问题是绕不开的重点,直接决定系统稳定性:
缓存穿透 :查询'不存在的数据'(如用户 ID=99999,数据库无记录),缓存无法命中,所有请求直接穿透到数据库。高并发场景下,大量无效请求会压垮数据库,导致服务不可用。
缓存击穿 :热点 Key(如爆款商品 ID、首页配置 Key)过期瞬间,大量并发请求同时穿透到数据库,导致数据库瞬时压力飙升,甚至触发熔断。
缓存雪崩 :某一时间段内,大量缓存 Key 集中过期(如凌晨 1 点批量更新缓存),或缓存集群故障(如 Redis 主从切换失败),所有请求全部穿透到数据库,引发数据库雪崩崩溃。
1.3 分布式缓存技术选型前提
分布式缓存的核心选型维度的是'性能、并发支持、集群适配、开发效率、运维成本',结合实际项目需求,形成两大技术路线:
Redis 原生客户端路线(Lettuce/Jedis) :轻量高效,专注于 Redis 基础命令的执行(缓存 CRUD),适合需要极致性能、简单缓存需求的场景;
Redisson 路线 :基于 Redis 封装的分布式服务框架,不仅能实现缓存功能,还提供分布式锁、延迟队列等高级功能,适合分布式架构下的复杂需求,开发效率更高。
两者并非替代关系,而是互补关系——实际项目中常'基础缓存用 Redis 原生客户端,高级功能用 Redisson',兼顾性能与开发效率。
二、本地缓存基础落地(Caffeine 首选)
2.1 主流本地缓存方案对比
本地缓存的核心诉求是'高性能、低开销',主流方案的选型逻辑如下(生产环境优先 Caffeine):
Caffeine 最优 完善(过期策略、容量限制、缓存统计、异步加载) 低 绝大多数生产场景(首选) 高 Guava Cache 良好 完善(过期策略、容量限制) 中 旧项目兼容、无需极致性能 中 ConcurrentHashMap 较高 基础(无过期、无淘汰机制) 低 简单临时缓存、少量固定数据 低
核心结论 :Caffeine 是当前 Java 本地缓存的最优选择——性能比 Guava Cache 快 10 倍以上(基于 W-TinyLFU 淘汰算法),支持更多工业级特性,且 Spring Boot 2.3+ 已默认集成,无需额外依赖。
2.2 工业级 Caffeine 配置与实战
(1)统一常量类(系列共用,标准化配置)
public class CacheConstants {
public static final String LOCAL_CACHE_PREFIX = "local:cache:" ;
public static final String DISTRIBUTED_CACHE_PREFIX = "distributed:cache:" ;
public static final int LOCAL_CACHE_MAX_SIZE = 10000 ;
public static final long LOCAL_CACHE_EXPIRE_SECONDS = 30 * 60 ;
public static final long LOCAL_CACHE_HOT_EXPIRE_SECONDS = 2 * 60 * 60 ;
public static final long DISTRIBUTED_CACHE_DEFAULT_EXPIRE_SECONDS = 60 * 60 ;
public static final long DISTRIBUTED_CACHE_HOT_EXPIRE_SECONDS = 3 * 60 * 60 ;
public static final long DELAY_DOUBLE_DELETE_MILLIS = 500 ;
public static final String LOCK_PREFIX = "distributed:lock:" ;
public static final long LOCK_WAIT_TIME = 10 ;
public static final long LOCK_LEASE_TIME = 30 ;
}
(2)Caffeine 配置类(Spring Bean 管理,全局复用) import com.github.benmanes.caffeine.cache.Caffeine;
import com.github.benmanes.caffeine.cache.LoadingCache;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
@Configuration
public class CaffeineConfig {
@Bean
public LoadingCache<String, Object> localCache () {
return Caffeine.newBuilder()
.maximumSize(CacheConstants.LOCAL_CACHE_MAX_SIZE)
.expireAfterWrite(CacheConstants.LOCAL_CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS)
.recordStats()
.executor(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()))
.removalListener((key, value, cause) -> System.out.printf("本地缓存移除:key=%s,原因=%s%n" , key, cause.name()))
.build(key -> null );
}
}
(3)本地缓存业务层封装(工业级规范) import com.github.benmanes.caffeine.cache.LoadingCache;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class LocalCacheService {
@Resource
private LoadingCache<String, Object> localCache;
public void put (String key, Object value) {
put(key, value, CacheConstants.LOCAL_CACHE_EXPIRE_SECONDS);
}
public void put (String key, Object value, long expireSeconds) {
if (key == null || value == null ) {
throw new IllegalArgumentException ("缓存 Key/Value 不能为空" );
}
String cacheKey = CacheConstants.LOCAL_CACHE_PREFIX + key;
localCache.put(cacheKey, value);
if (expireSeconds > CacheConstants.LOCAL_CACHE_EXPIRE_SECONDS) {
localCache.policy().expireAfterWrite().ifPresent(policy -> policy.setExpiresAfter(cacheKey, expireSeconds, TimeUnit.SECONDS));
}
}
public Object get (String key) {
if (key == null ) {
return null ;
}
String cacheKey = CacheConstants.LOCAL_CACHE_PREFIX + key;
try {
return localCache.get(cacheKey);
} catch (Exception e) {
System.err.printf("本地缓存查询异常:key=%s,异常信息=%s%n" , key, e.getMessage());
return null ;
}
}
public void remove (String key) {
if (key == null ) {
return ;
}
String cacheKey = CacheConstants.LOCAL_CACHE_PREFIX + key;
localCache.invalidate(cacheKey);
}
public void removeBatch (Iterable<String> keys) {
if (keys == null ) {
return ;
}
Iterable<String> cacheKeys = () -> keys.iterator().forEachRemaining(k -> CacheConstants.LOCAL_CACHE_PREFIX + k);
localCache.invalidateAll(cacheKeys);
}
public String getCacheStats () {
com.github.benmanes.caffeine.cache.Stats stats = localCache.stats();
return String.format("本地缓存命中率:%.2f%%,缺失率:%.2f%%,加载成功数:%d,加载失败数:%d" , stats.hitRate() * 100 , stats.missRate() * 100 , stats.loadSuccessCount(), stats.loadFailureCount());
}
}
三、分布式缓存方案一:Redis 原生客户端实现(Lettuce+Jedis)
3.1 Redis 客户端深度对比(Lettuce vs Jedis) Redis 原生客户端是直接与 Redis 交互的工具,核心对比决定了选型优先级:
对比维度 Lettuce(Spring Boot 2.x+ 默认) Jedis(传统客户端) 线程模型 异步非阻塞(基于 Netty 驱动) 阻塞式 IO(BIO) 并发性能 高(单连接支持多线程并发,连接复用) 中(单线程独占一个连接,需手动管理连接池) 集群支持 原生支持(集群/哨兵/单机模式无缝切换) 需额外引入 jedis-cluster 依赖,手动适配集群 开发成本 低(Spring Data Redis 原生整合,零配置) 中(需手动配置连接池,处理线程安全) 序列化支持 支持自定义序列化(Fastjson2/Jackson) 需手动封装序列化逻辑 适用场景 高并发、分布式集群、Spring Boot 项目、核心业务 旧项目迁移、低并发场景、需直接操作 Redis 原生命令 避坑要点 1. 连接池参数优化(避免连接耗尽);2. 超时时间配置(避免无限阻塞);3. 集群分片 Key 设计 1. 避免连接泄露(用完必须归还连接池);2. 控制并发数(避免连接池过载);3. 手动处理主从切换
核心结论 :无特殊场景一律选择 Lettuce——Spring Boot 默认集成,开发成本低,异步非阻塞模型适配高并发,集群支持完善;Jedis 仅作为'旧项目兼容'或'需原生命令操作'的备用方案。
3.2 Lettuce 工业级实现(首选方案)
(1)前置依赖(Spring Boot 原生整合)
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-data-redis</artifactId >
</dependency >
<dependency >
<groupId > com.alibaba</groupId >
<artifactId > fastjson2</artifactId >
<version > 2.0.41</version >
</dependency >
(2)工业级配置(application.yml) spring:
redis:
cluster:
nodes:
- 192.168 .1 .100 :6379
- 192.168 .1 .101 :6379
- 192.168 .1 .102 :6379
max-redirects: 3
password: your-redis-password
timeout: 3000ms
database: 0
lettuce:
pool:
max-active: 16
max-idle: 8
min-idle: 4
max-wait: -1ms
shutdown-timeout: 100ms
(3)RedisTemplate 配置(序列化优化) 默认 RedisTemplate 使用 JDK 序列化,会导致存储乱码、占用空间大,需替换为 Fastjson2 序列化:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONWriter;
import com.alibaba.fastjson2.serializer.SerializerFeature;
@Configuration
public class RedisLettuceConfig {
@Bean
public RedisTemplate<String, Object> redisTemplate (RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate <>();
redisTemplate.setConnectionFactory(connectionFactory);
StringRedisSerializer keySerializer = new StringRedisSerializer ();
redisTemplate.setKeySerializer(keySerializer);
redisTemplate.setHashKeySerializer(keySerializer);
Fastjson2RedisSerializer valueSerializer = new Fastjson2RedisSerializer (Object.class);
redisTemplate.setValueSerializer(valueSerializer);
redisTemplate.setHashValueSerializer(valueSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
public static class Fastjson2RedisSerializer <T> extends org .springframework.data.redis.serializer.RedisSerializer<T> {
private final Class<T> clazz;
public Fastjson2RedisSerializer (Class<T> clazz) {
this .clazz = clazz;
}
@Override
public byte [] serialize(T t) {
if (t == null ) {
return new byte [0 ];
}
return JSON.toJSONBytes(t, JSONWriter.Feature.WriteDateUseDateFormat, JSONWriter.Feature.DisableCircularReferenceDetect, JSONWriter.Feature.WriteNullsAsEmpty);
}
@Override
public T deserialize (byte [] bytes) {
if (bytes == null || bytes.length == 0 ) {
return null ;
}
return JSON.parseObject(bytes, clazz);
}
}
}
(4)Lettuce 业务层封装(工业级规范) import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Service
public class RedisLettuceCacheService {
@Resource
private RedisTemplate<String, Object> redisTemplate;
public void put (String key, Object value) {
put(key, value, CacheConstants.DISTRIBUTED_CACHE_DEFAULT_EXPIRE_SECONDS);
}
public void put (String key, Object value, long expireSeconds) {
if (key == null || value == null ) {
throw new IllegalArgumentException ("缓存 Key/Value 不能为空" );
}
String cacheKey = CacheConstants.DISTRIBUTED_CACHE_PREFIX + key;
try {
redisTemplate.opsForValue().set(cacheKey, value, expireSeconds, TimeUnit.SECONDS);
} catch (Exception e) {
System.err.printf("Lettuce 缓存写入异常:key=%s,异常信息=%s%n" , key, e.getMessage());
}
}
public void putNullValue (String key) {
put(key, new NullValue (), 60 );
}
public Object get (String key) {
if (key == null ) {
return null ;
}
String cacheKey = CacheConstants.DISTRIBUTED_CACHE_PREFIX + key;
try {
Object value = redisTemplate.opsForValue().get(cacheKey);
return value instanceof NullValue ? null : value;
} catch (Exception e) {
System.err.printf("Lettuce 缓存查询异常:key=%s,异常信息=%s%n" , key, e.getMessage());
return null ;
}
}
public void remove (String key) {
if (key == null ) {
return ;
}
String cacheKey = CacheConstants.DISTRIBUTED_CACHE_PREFIX + key;
try {
redisTemplate.delete(cacheKey);
} catch (Exception e) {
System.err.printf("Lettuce 缓存删除异常:key=%s,异常信息=%s%n" , key, e.getMessage());
}
}
public void removeBatch (Iterable<String> keys) {
if (keys == null ) {
return ;
}
Iterable<String> cacheKeys = () -> keys.iterator().forEachRemaining(k -> CacheConstants.DISTRIBUTED_CACHE_PREFIX + k);
try {
redisTemplate.delete(cacheKeys);
} catch (Exception e) {
System.err.printf("Lettuce 缓存批量删除异常,异常信息=%s%n" , e.getMessage());
}
}
public void delayDoubleRemove (String key) {
remove(key);
CacheThreadPool.DELAY_DELETE_EXECUTOR.schedule(() -> remove(key), CacheConstants.DELAY_DOUBLE_DELETE_MILLIS, TimeUnit.MILLISECONDS);
}
private static class NullValue {}
}
class CacheThreadPool {
public static final java.util.concurrent.ScheduledExecutorService DELAY_DELETE_EXECUTOR = java.util.concurrent.Executors.newScheduledThreadPool(5 , new java .util.concurrent.ThreadFactory() {
private int count = 0 ;
@Override
public Thread newThread (Runnable r) {
Thread thread = new Thread (r);
thread.setName("cache-delay-delete-" + (++count));
thread.setDaemon(true );
return thread;
}
});
}
3.3 Jedis 工业级实现(备用方案)
(1)前置依赖(排除 Lettuce,引入 Jedis)
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-data-redis</artifactId >
<exclusions >
<exclusion >
<groupId > io.lettuce</groupId >
<artifactId > lettuce-core</artifactId >
</exclusion >
</exclusions >
</dependency >
<dependency >
<groupId > redis.clients</groupId >
<artifactId > jedis</artifactId >
<version > 4.4.6</version >
</dependency >
<dependency >
<groupId > com.alibaba</groupId >
<artifactId > fastjson2</artifactId >
<version > 2.0.41</version >
</dependency >
(2)Jedis 配置类(连接池+RedisTemplate) import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import redis.clients.jedis.JedisPoolConfig;
@Configuration
public class RedisJedisConfig {
@Bean
public JedisPoolConfig jedisPoolConfig () {
JedisPoolConfig poolConfig = new JedisPoolConfig ();
poolConfig.setMaxTotal(16 );
poolConfig.setMaxIdle(8 );
poolConfig.setMinIdle(4 );
poolConfig.setMaxWaitMillis(-1 );
poolConfig.setTestOnBorrow(true );
poolConfig.setTestOnReturn(true );
return poolConfig;
}
@Bean
public RedisConnectionFactory redisConnectionFactory (JedisPoolConfig poolConfig) {
JedisConnectionFactory factory = new JedisConnectionFactory (poolConfig);
factory.setHostName("192.168.1.100" );
factory.setPort(6379 );
factory.setPassword("your-redis-password" );
factory.setTimeout(3000 );
factory.setDatabase(0 );
return factory;
}
@Bean
public RedisTemplate<String, Object> redisTemplate (RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate <>();
redisTemplate.setConnectionFactory(connectionFactory);
StringRedisSerializer keySerializer = new StringRedisSerializer ();
redisTemplate.setKeySerializer(keySerializer);
redisTemplate.setHashKeySerializer(keySerializer);
RedisLettuceConfig.Fastjson2RedisSerializer valueSerializer = new RedisLettuceConfig .Fastjson2RedisSerializer(Object.class);
redisTemplate.setValueSerializer(valueSerializer);
redisTemplate.setHashValueSerializer(valueSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
(3)Jedis 业务层封装(与 Lettuce API 对齐) import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Service
public class RedisJedisCacheService {
@Resource
private RedisTemplate<String, Object> redisTemplate;
public void put (String key, Object value) {
put(key, value, CacheConstants.DISTRIBUTED_CACHE_DEFAULT_EXPIRE_SECONDS);
}
public void put (String key, Object value, long expireSeconds) {
if (key == null || value == null ) {
throw new IllegalArgumentException ("缓存 Key/Value 不能为空" );
}
String cacheKey = CacheConstants.DISTRIBUTED_CACHE_PREFIX + key;
try {
redisTemplate.opsForValue().set(cacheKey, value, expireSeconds, TimeUnit.SECONDS);
} catch (Exception e) {
System.err.printf("Jedis 缓存写入异常:key=%s,异常信息=%s%n" , key, e.getMessage());
}
}
public void putNullValue (String key) {
put(key, new RedisLettuceCacheService .NullValue(), 60 );
}
public Object get (String key) {
if (key == null ) {
return null ;
}
String cacheKey = CacheConstants.DISTRIBUTED_CACHE_PREFIX + key;
try {
Object value = redisTemplate.opsForValue().get(cacheKey);
return value instanceof RedisLettuceCacheService.NullValue ? null : value;
} catch (Exception e) {
System.err.printf("Jedis 缓存查询异常:key=%s,异常信息=%s%n" , key, e.getMessage());
return null ;
}
}
public void remove (String key) {
if (key == null ) {
return ;
}
String cacheKey = CacheConstants.DISTRIBUTED_CACHE_PREFIX + key;
try {
redisTemplate.delete(cacheKey);
} catch (Exception e) {
System.err.printf("Jedis 缓存删除异常:key=%s,异常信息=%s%n" , key, e.getMessage());
}
}
public void delayDoubleRemove (String key) {
remove(key);
CacheThreadPool.DELAY_DELETE_EXECUTOR.schedule(() -> remove(key), CacheConstants.DELAY_DOUBLE_DELETE_MILLIS, TimeUnit.MILLISECONDS);
}
}
四、分布式缓存方案二:Redisson 实现(分布式高级功能首选)
4.1 Redisson 核心定位与优势 Redisson 不是单纯的 Redis 客户端,而是「基于 Redis 构建的分布式服务框架」——它将 Redis 的基础命令封装成了开箱即用的分布式工具,核心优势如下:
高级功能丰富 :内置分布式锁、延迟队列、分布式集合、布隆过滤器等,无需手动封装(如分布式锁的自动续期、红锁实现);
API 极度友好 :完全屏蔽 Redis 原生命令,用面向对象的方式操作(如 RLock.lock()、RMap.put()),学习成本低;
高可用设计 :自动处理连接重试、锁过期、集群故障转移,无需手动编写容错逻辑;
集群原生支持 :无缝适配 Redis 单机、哨兵、分片集群,配置简单;
缓存功能完善 :支持过期策略、缓存淘汰、持久化,兼顾基础缓存与高级功能。
核心定位 :适合分布式架构下需要高级功能(如分布式锁、延迟队列)的场景,或追求快速开发、降低容错成本的项目。
4.2 Redisson 工业级配置
(1)前置依赖(Redisson Spring Boot Starter)
<dependency >
<groupId > org.redisson</groupId >
<artifactId > redisson-spring-boot-starter</artifactId >
<version > 3.23.5</version >
</dependency >
<dependency >
<groupId > com.alibaba</groupId >
<artifactId > fastjson2</artifactId >
<version > 2.0.41</version >
</dependency >
(2)application.yml 配置(集群/单机兼容) spring:
redis:
password: your-redis-password
timeout: 3000ms
redisson:
config: |
singleServerConfig:
address: "redis://192.168.1.100:6379" # 单机模式(集群模式替换为 clusterServersConfig)
password: "your-redis-password"
timeout: 3000
connectionPoolSize: 16 # 连接池大小(与 Lettuce 保持一致)
connectionMinimumIdleSize: 4 # 最小空闲连接数
# 集群模式配置(替换 singleServerConfig)
# clusterServersConfig:
# nodeAddresses:
# - "redis://192.168.1.100:6379"
# - "redis://192.168.1.101:6379"
# password: "your-redis-password"
# timeout: 3000
# scanInterval: 2000 # 集群节点扫描间隔
serializer:
# 序列化配置(与 Redis 客户端保持一致,避免数据不一致)
type: org.redisson.codec.FastJson2Codec
(3)RedissonClient 配置类(Spring Bean 管理) import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class RedissonConfig {
@Value("${redisson.config}")
private String redissonConfig;
@Bean(destroyMethod = "shutdown")
public RedissonClient redissonClient () {
Config config = Config.fromYAML(redissonConfig);
return Redisson.create(config);
}
}
4.3 Redisson 缓存与高级功能实战
(1)Redisson 基础缓存实现(分布式 Map) import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Service
public class RedissonCacheService {
@Resource
private RedissonClient redissonClient;
private <K, V> RMap<K, V> getCacheMap () {
return redissonClient.getMap(CacheConstants.DISTRIBUTED_CACHE_PREFIX);
}
public void put (String key, Object value) {
put(key, value, CacheConstants.DISTRIBUTED_CACHE_DEFAULT_EXPIRE_SECONDS);
}
public void put (String key, Object value, long expireSeconds) {
if (key == null || value == null ) {
throw new IllegalArgumentException ("缓存 Key/Value 不能为空" );
}
RMap<String, Object> cacheMap = getCacheMap();
try {
cacheMap.put(key, value);
cacheMap.expire(key, expireSeconds, TimeUnit.SECONDS);
} catch (Exception e) {
System.err.printf("Redisson 缓存写入异常:key=%s,异常信息=%s%n" , key, e.getMessage());
}
}
public void putNullValue (String key) {
put(key, new NullValue (), 60 );
}
public Object get (String key) {
if (key == null ) {
return null ;
}
RMap<String, Object> cacheMap = getCacheMap();
try {
Object value = cacheMap.get(key);
return value instanceof NullValue ? null : value;
} catch (Exception e) {
System.err.printf("Redisson 缓存查询异常:key=%s,异常信息=%s%n" , key, e.getMessage());
return null ;
}
}
public void remove (String key) {
if (key == null ) {
return ;
}
RMap<String, Object> cacheMap = getCacheMap();
try {
cacheMap.remove(key);
} catch (Exception e) {
System.err.printf("Redisson 缓存删除异常:key=%s,异常信息=%s%n" , key, e.getMessage());
}
}
private static class NullValue {}
}
(2)Redisson 核心高级功能:分布式锁(解决缓存击穿) import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Component
public class RedissonLockUtil {
@Resource
private RedissonClient redissonClient;
public RLock tryLock (String lockKey) {
return tryLock(lockKey, CacheConstants.LOCK_WAIT_TIME, CacheConstants.LOCK_LEASE_TIME);
}
public RLock tryLock (String lockKey, long waitTime, long leaseTime) {
if (lockKey == null ) {
throw new IllegalArgumentException ("锁 Key 不能为空" );
}
String key = CacheConstants.LOCK_PREFIX + lockKey;
RLock lock = redissonClient.getLock(key);
try {
boolean locked = lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
return locked ? lock : null ;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null ;
}
}
public void unlock (RLock lock) {
if (lock != null && lock.isHeldByCurrentThread()) {
try {
lock.unlock();
} catch (Exception e) {
System.err.printf("释放分布式锁异常,异常信息=%s%n" , e.getMessage());
}
}
}
}
(3)Redisson 核心高级功能:延迟队列(优化延迟双删) import org.redisson.api.RDelayedQueue;
import org.redisson.api.RQueue;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
@Component
public class RedissonDelayQueueUtil {
@Resource
private RedissonClient redissonClient;
private static final String DELAY_QUEUE_NAME = "distributed:delay:queue" ;
public <T> void addDelayTask (T task, long delay, TimeUnit timeUnit) {
if (task == null ) {
throw new IllegalArgumentException ("任务内容不能为空" );
}
RQueue<T> queue = redissonClient.getQueue(DELAY_QUEUE_NAME);
RDelayedQueue<T> delayedQueue = redissonClient.getDelayedQueue(queue);
delayedQueue.offer(task, delay, timeUnit);
}
public <T> void listenDelayTask (Class<T> taskClass, DelayTaskHandler<T> handler) {
RQueue<T> queue = redissonClient.getQueue(DELAY_QUEUE_NAME);
RDelayedQueue<T> delayedQueue = redissonClient.getDelayedQueue(queue);
while (true ) {
try {
T task = queue.take();
if (task != null ) {
handler.handle(task);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
break ;
} catch (Exception e) {
System.err.printf("处理延迟任务异常,异常信息=%s%n" , e.getMessage());
}
}
}
@FunctionalInterface
public interface DelayTaskHandler <T> {
void handle (T task) ;
}
}
class CacheDelayDeleteTask {
private String key;
}
@Component
class DelayQueueInitializer {
@Resource
private RedissonDelayQueueUtil delayQueueUtil;
@Resource
private RedissonCacheService redissonCacheService;
public void init () {
delayQueueUtil.listenDelayTask(CacheDelayDeleteTask.class, task -> {
String key = task.getKey();
redissonCacheService.remove(key);
System.out.printf("延迟双删任务执行:key=%s%n" , key);
});
}
}
五、核心选型对比与场景适配(重点)
5.1 三大方案全面对比表 方案 性能 开发效率 集群支持 高级功能 运维成本 学习成本 适用场景 Redis+Lettuce 高 低 - 中 原生支持 无 低 中 高并发核心业务、Spring Boot 项目、基础缓存 CRUD、集群部署 Redis+Jedis 中 中 需适配 无 中 中 旧项目迁移、低并发场景、需直接操作 Redis 原生命令 Redisson 中 - 高 高 原生支持 丰富(分布式锁/延迟队列等) 低 低 分布式架构、快速开发、需高级功能、中小并发业务
5.2 典型场景选型指南(直接落地参考)
高并发核心业务(如电商商品详情、支付接口)
选型:Caffeine+Redis+Lettuce
理由:Lettuce 异步非阻塞模型支撑高并发,Caffeine 减少网络开销,性能最优;
补充:用 Redisson 分布式锁解决缓存击穿(热点 Key 过期)。
旧项目缓存改造(已集成 Jedis)
选型:Caffeine+Redis+Jedis
理由:无需大规模修改代码,优化 Jedis 连接池参数即可提升性能;
避坑:确保连接池复用,避免连接泄露。
分布式架构 + 快速开发(如中台系统、内部工具)
选型:Caffeine+Redisson
理由:Redisson 开箱即用分布式锁、延迟队列,开发效率高,无需手动封装;
优势:兼顾基础缓存与高级功能,减少依赖冲突。
分布式锁/延迟队列等高级需求(如秒杀、订单超时取消)
选型:Redisson(必选)
理由:Redisson 红锁解决单点故障,自动续期避免死锁,延迟队列稳定可靠;
替代方案:Redis+Lettuce 需手动封装,容错成本高。
中小并发、单体应用(如管理后台、工具类项目)
选型:Caffeine+Redis+Lettuce 或 Caffeine+Redisson
理由:配置简单,无需复杂集群,满足性能需求即可。
需直接操作 Redis 原生命令(如自定义协议、特殊命令)
选型:Redis+Jedis 或 Redis+Lettuce(execute 方法)
理由:Jedis API 更贴近原生命令,Lettuce 需通过 RedisConnection.execute() 调用。
5.3 选型决策流程(三步法)
看并发量:高并发(1 万 QPS 以上)→ 优先 Lettuce;中低并发→ 可选 Redisson/Jedis;
看功能需求:需分布式锁/延迟队列→ Redisson;仅基础缓存→ Lettuce/Jedis;
看技术栈:Spring Boot 2.x+→ 优先 Lettuce;旧项目→ 优先 Jedis;快速开发→ Redisson。
六、工业级落地实战:多级缓存全链路整合
6.1 方案一:Caffeine+Redis+Lettuce(高并发首选)
(1)全链路流程
查询流程 :
本地缓存(Caffeine)→ 命中返回 → 未命中 → Redis(Lettuce)→ 命中返回(同步到本地缓存)→ 未命中 → 分布式锁(Redisson)→ 数据库 → 回写 Redis+ 本地缓存 → 返回;
更新流程 :
数据库更新 → 删除本地缓存 → Redis 删除(Lettuce)→ 延迟双删(线程池)→ 返回。
(2)实战代码(商品查询示例) import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.UUID;
@Service
public class ProductService {
@Resource
private LocalCacheService localCacheService;
@Resource
private RedisLettuceCacheService lettuceCacheService;
@Resource
private RedissonLockUtil redissonLockUtil;
@Resource
private ProductDAO productDAO;
public ProductDTO getProductById (Long productId) {
if (productId == null || productId <= 0 ) {
return null ;
}
String cacheKey = "product:" + productId;
ProductDTO productDTO;
productDTO = (ProductDTO) localCacheService.get(cacheKey);
if (productDTO != null ) {
System.out.printf("本地缓存命中:key=%s%n" , cacheKey);
return productDTO;
}
productDTO = (ProductDTO) lettuceCacheService.get(cacheKey);
if (productDTO != null ) {
localCacheService.put(cacheKey, productDTO);
System.out.printf("Redis 缓存命中:key=%s%n" , cacheKey);
return productDTO;
}
String lockKey = "product:lock:" + productId;
RLock lock = redissonLockUtil.tryLock(lockKey);
if (lock == null ) {
return null ;
}
try {
productDTO = (ProductDTO) lettuceCacheService.get(cacheKey);
if (productDTO != null ) {
localCacheService.put(cacheKey, productDTO);
return productDTO;
}
productDTO = productDAO.selectById(productId);
if (productDTO != null ) {
long expireSeconds = CacheConstants.DISTRIBUTED_CACHE_HOT_EXPIRE_SECONDS;
lettuceCacheService.put(cacheKey, productDTO, expireSeconds);
localCacheService.put(cacheKey, productDTO, CacheConstants.LOCAL_CACHE_HOT_EXPIRE_SECONDS);
} else {
lettuceCacheService.putNullValue(cacheKey);
localCacheService.put(cacheKey, new RedisLettuceCacheService .NullValue());
}
} finally {
redissonLockUtil.unlock(lock);
}
return productDTO;
}
public boolean updateProduct (ProductDTO productDTO) {
if (productDTO == null || productDTO.getId() == null ) {
return false ;
}
String cacheKey = "product:" + productDTO.getId();
try {
boolean success = productDAO.update(productDTO);
if (!success) {
return false ;
}
localCacheService.remove(cacheKey);
lettuceCacheService.delayDoubleRemove(cacheKey);
return true ;
} catch (Exception e) {
System.err.printf("更新商品异常:id=%s,异常=%s%n" , productDTO.getId(), e.getMessage());
return false ;
}
}
}
6.2 方案二:Caffeine+Redisson(快速开发首选)
(1)全链路流程
查询流程 :本地缓存(Caffeine)→ 命中返回 → 未命中 → Redisson 分布式 Map → 命中返回(同步到本地缓存)→ 未命中 → Redisson 分布式锁 → 数据库 → 回写双缓存 → 返回;
更新流程 :数据库更新 → 删除本地缓存 → Redisson 分布式 Map 删除 → Redisson 延迟队列二次删除 → 返回。
(2)实战代码(简化版商品查询) import org.redisson.api.RLock;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
@Service
public class ProductRedissonService {
@Resource
private LocalCacheService localCacheService;
@Resource
private RedissonCacheService redissonCacheService;
@Resource
private RedissonLockUtil redissonLockUtil;
@Resource
private ProductDAO productDAO;
public ProductDTO getProductById (Long productId) {
if (productId == null || productId <= 0 ) {
return null ;
}
String cacheKey = "product:" + productId;
ProductDTO productDTO;
productDTO = (ProductDTO) localCacheService.get(cacheKey);
if (productDTO != null ) {
return productDTO;
}
productDTO = (ProductDTO) redissonCacheService.get(cacheKey);
if (productDTO != null ) {
localCacheService.put(cacheKey, productDTO);
return productDTO;
}
RLock lock = redissonLockUtil.tryLock(cacheKey);
if (lock == null ) {
return null ;
}
try {
productDTO = (ProductDTO) redissonCacheService.get(cacheKey);
if (productDTO != null ) {
localCacheService.put(cacheKey, productDTO);
return productDTO;
}
productDTO = productDAO.selectById(productId);
if (productDTO != null ) {
redissonCacheService.put(cacheKey, productDTO, CacheConstants.DISTRIBUTED_CACHE_HOT_EXPIRE_SECONDS);
localCacheService.put(cacheKey, productDTO, CacheConstants.LOCAL_CACHE_HOT_EXPIRE_SECONDS);
} else {
redissonCacheService.putNullValue(cacheKey);
localCacheService.put(cacheKey, new RedissonCacheService .NullValue());
}
} finally {
redissonLockUtil.unlock(lock);
}
return productDTO;
}
}
6.3 缓存三大问题解决方案落地(分方案) 问题类型 Redis+Lettuce/Jedis 解决方案 Redisson 解决方案 缓存穿透 1. 空值缓存(1 分钟过期);2. 参数校验;3. 后续补充布隆过滤器 1. 空值缓存;2. 参数校验;3. Redisson 布隆过滤器 缓存击穿 1. Redis SET NX 命令实现互斥锁;2. 热点 Key 延长过期时间 1. 分布式可重入红锁(自动续期);2. 热点 Key 延长过期 缓存雪崩 1. 过期时间随机化(±30 秒);2. Redis 集群(主从 + 哨兵);3. Sentinel 熔断降级 1. 过期时间随机化;2. Redis 集群;3. Redisson 熔断降级;4. 缓存预热
七、总结与下一篇预告
7.1 核心收获 本文围绕'Java 工业级多级缓存',落地了三大技术方案,核心收获如下:
掌握本地缓存(Caffeine)的工业级配置与封装,理解其'高性能、低开销'的核心优势;
精通 Redis 原生客户端(Lettuce/Jedis)的实现细节,明确 Lettuce 的首选地位与 Jedis 的备用场景;
学会 Redisson 的核心用法,利用其分布式锁、延迟队列等高级功能解决缓存击穿、同步延迟等痛点;
明确不同场景的选型逻辑,能根据并发量、功能需求快速选择最优方案,并实现全链路整合;
解决缓存三大经典问题,形成'参数校验 + 空值缓存 + 分布式锁 + 过期随机化'的基础防护体系。
7.2 下一篇预告 本文的空值缓存方案在'海量无效 Key 场景'下存在明显局限:
大量空值缓存占用 Redis 宝贵内存;
空值缓存存在'过期窗口',窗口期内的无效请求仍会穿透到数据库。
下一篇《Java 工业级缓存实战系列(二):缓存穿透终极解决方案——布隆过滤器(Redisson+Redis Bloom)》将聚焦:
布隆过滤器核心原理(二进制向量 + 多哈希函数);
双方案落地:Redisson 布隆过滤器(快速开发)与 Redis Bloom+Lettuce(极致性能);
布隆过滤器与本文多级缓存架构的无缝整合,形成'布隆拦截 + 空值缓存 + 多级缓存'的三重防护体系。
7.3 实战扩展建议
序列化统一 :所有方案统一使用 Fastjson2 或 Jackson,避免不同客户端序列化不一致导致的脏数据;
动态配置 :将缓存容量、过期时间、连接池参数等配置到 Nacos/Apollo,支持动态调整,无需重启应用;
监控告警 :接入 Prometheus+Grafana,监控缓存命中率(目标≥90%)、Redis 内存使用率(阈值≤70%)、分布式锁竞争率,设置告警阈值;
压测验证 :针对高并发场景做压测,验证 Lettuce 连接池参数、Redisson 锁性能,提前发现瓶颈;
缓存预热 :应用启动时,通过 CommandLineRunner 批量加载热点数据到 Caffeine+Redis,避免冷启动穿透。
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online