Java工业级缓存实战系列(一):多级缓存架构设计与落地(Redis客户端+Redisson全方案)
-
- 前言
- 一、缓存核心认知与架构设计
-
- [1.1 缓存核心分类与价值](#1.1 缓存核心分类与价值)
- [1.2 缓存三大核心问题(定义+业务影响)](#1.2 缓存三大核心问题(定义+业务影响))
- [1.3 分布式缓存技术选型前提](#1.3 分布式缓存技术选型前提)
- 二、本地缓存基础落地(Caffeine首选)
-
- [2.1 主流本地缓存方案对比](#2.1 主流本地缓存方案对比)
- [2.2 工业级Caffeine配置与实战](#2.2 工业级Caffeine配置与实战)
-
- (1)统一常量类(系列共用,标准化配置)
- [(2)Caffeine配置类(Spring Bean管理,全局复用)](#(2)Caffeine配置类(Spring Bean管理,全局复用))
- (3)本地缓存业务层封装(工业级规范)
- 三、分布式缓存方案一:Redis原生客户端实现(Lettuce+Jedis)
-
- [3.1 Redis客户端深度对比(Lettuce vs Jedis)](#3.1 Redis客户端深度对比(Lettuce vs Jedis))
- [3.2 Lettuce工业级实现(首选方案)](#3.2 Lettuce工业级实现(首选方案))
-
- [(1)前置依赖(Spring Boot原生整合)](#(1)前置依赖(Spring Boot原生整合))
- (2)工业级配置(application.yml)
- (3)RedisTemplate配置(序列化优化)
- (4)Lettuce业务层封装(工业级规范)
- [3.3 Jedis工业级实现(备用方案)](#3.3 Jedis工业级实现(备用方案))
-
- (1)前置依赖(排除Lettuce,引入Jedis)
- (2)Jedis配置类(连接池+RedisTemplate)
- [(3)Jedis业务层封装(与Lettuce API对齐)](#(3)Jedis业务层封装(与Lettuce API对齐))
- 四、分布式缓存方案二:Redisson实现(分布式高级功能首选)
-
- [4.1 Redisson核心定位与优势](#4.1 Redisson核心定位与优势)
- [4.2 Redisson工业级配置](#4.2 Redisson工业级配置)
-
- [(1)前置依赖(Redisson Spring Boot Starter)](#(1)前置依赖(Redisson Spring Boot Starter))
- (2)application.yml配置(集群/单机兼容)
- [(3)RedissonClient配置类(Spring Bean管理)](#(3)RedissonClient配置类(Spring Bean管理))
- [4.3 Redisson缓存与高级功能实战](#4.3 Redisson缓存与高级功能实战)
- 五、核心选型对比与场景适配(重点)
-
- [5.1 三大方案全面对比表](#5.1 三大方案全面对比表)
- [5.2 典型场景选型指南(直接落地参考)](#5.2 典型场景选型指南(直接落地参考))
- [5.3 选型决策流程(三步法)](#5.3 选型决策流程(三步法))
- 六、工业级落地实战:多级缓存全链路整合
-
- [6.1 方案一:Caffeine+Redis+Lettuce(高并发首选)](#6.1 方案一:Caffeine+Redis+Lettuce(高并发首选))
- [6.2 方案二:Caffeine+Redisson(快速开发首选)](#6.2 方案二:Caffeine+Redisson(快速开发首选))
- [6.3 缓存三大问题解决方案落地(分方案)](#6.3 缓存三大问题解决方案落地(分方案))
- 七、总结与下一篇预告
-
- [7.1 核心收获](#7.1 核心收获)
- [7.2 下一篇预告](#7.2 下一篇预告)
- [7.3 实战扩展建议](#7.3 实战扩展建议)
前言
在高并发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)统一常量类(系列共用,标准化配置)
java
/**
* 缓存统一常量类(本地缓存+分布式缓存共用)
*/
public class CacheConstants {
// 本地缓存前缀(避免Key冲突)
public static final String LOCAL_CACHE_PREFIX = "local:cache:";
// 分布式缓存前缀(Redis/Redisson共用)
public static final String DISTRIBUTED_CACHE_PREFIX = "distributed:cache:";
// 本地缓存核心配置
public static final int LOCAL_CACHE_MAX_SIZE = 10000; // 最大容量(JVM堆内存10%-15%)
public static final long LOCAL_CACHE_EXPIRE_SECONDS = 30 * 60; // 写入后过期(30分钟)
public static final long LOCAL_CACHE_HOT_EXPIRE_SECONDS = 2 * 60 * 60; // 热点数据过期(2小时)
// 分布式缓存核心配置
public static final long DISTRIBUTED_CACHE_DEFAULT_EXPIRE_SECONDS = 60 * 60; // 默认过期(1小时)
public static final long DISTRIBUTED_CACHE_HOT_EXPIRE_SECONDS = 3 * 60 * 60; // 热点数据过期(3小时)
public static final long DELAY_DOUBLE_DELETE_MILLIS = 500; // 延迟双删时间(500毫秒)
// 分布式锁配置
public static final String LOCK_PREFIX = "distributed:lock:";
public static final long LOCK_WAIT_TIME = 10; // 锁等待时间(10秒)
public static final long LOCK_LEASE_TIME = 30; // 锁持有时间(30秒)
}
(2)Caffeine配置类(Spring Bean管理,全局复用)
java
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(工业级最优配置)
*/
@Bean
public LoadingCache<String, Object> localCache() {
return Caffeine.newBuilder()
// 最大容量(避免OOM,根据JVM堆内存调整)
.maximumSize(CacheConstants.LOCAL_CACHE_MAX_SIZE)
// 写入后过期策略(平衡一致性与性能)
.expireAfterWrite(CacheConstants.LOCAL_CACHE_EXPIRE_SECONDS, TimeUnit.SECONDS)
// 开启缓存统计(命中率、缺失率,用于监控优化)
.recordStats()
// 异步加载线程池(核心线程数=CPU核心数,避免阻塞业务线程)
.executor(Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()))
// 缓存移除监听器(日志打印,便于排查缓存失效原因)
.removalListener((key, value, cause) ->
System.out.printf("本地缓存移除:key=%s,原因=%s%n", key, cause.name()))
// 缓存缺失时的加载逻辑(此处留空,业务层手动实现,灵活度更高)
.build(key -> null);
}
}
(3)本地缓存业务层封装(工业级规范)
java
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;
/**
* 缓存存入(自动拼接前缀,避免Key冲突)
*/
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));
}
}
/**
* 缓存获取(未命中返回null,异常降级避免影响业务)
*/
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原生整合)
无需额外引入Lettuce依赖,Spring Boot Starter Data Redis已默认集成:
xml
<!-- Spring Data Redis(默认集成Lettuce) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 序列化依赖(Fastjson2,解决Redis存储乱码问题) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.41</version>
</dependency>
(2)工业级配置(application.yml)
yaml
spring:
redis:
# 集群配置(单机模式:直接配置host和port,无需cluster节点)
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 # 选择Redis数据库(默认0,按业务隔离)
# Lettuce连接池配置(核心优化,提升并发性能)
lettuce:
pool:
max-active: 16 # 最大连接数(CPU核心数*2,避免连接池耗尽)
max-idle: 8 # 最大空闲连接数(与max-active保持一致,减少连接创建开销)
min-idle: 4 # 最小空闲连接数(保证基础并发需求)
max-wait: -1ms # 最大等待时间(-1表示无限制,避免线程阻塞)
shutdown-timeout: 100ms # 关闭超时时间(优雅关闭连接,避免资源泄露)
(3)RedisTemplate配置(序列化优化)
默认RedisTemplate使用JDK序列化,会导致存储乱码、占用空间大,需替换为Fastjson2序列化:
java
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);
// 1. Key序列化:StringRedisSerializer(避免Key乱码,兼容Redis命令行查询)
StringRedisSerializer keySerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(keySerializer);
redisTemplate.setHashKeySerializer(keySerializer);
// 2. Value序列化:Fastjson2(高效、无乱码、支持复杂对象)
Fastjson2RedisSerializer valueSerializer = new Fastjson2RedisSerializer(Object.class);
redisTemplate.setValueSerializer(valueSerializer);
redisTemplate.setHashValueSerializer(valueSerializer);
// 3. 初始化RedisTemplate(必须调用,否则配置不生效)
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
/**
* 自定义Fastjson2 Redis序列化器(工业级配置)
*/
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业务层封装(工业级规范)
java
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* Redis Lettuce客户端业务层(基础缓存CRUD,高并发首选)
*/
@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); // 空值缓存1分钟,避免占用过多内存
}
/**
* 缓存获取(未命中返回null,空值缓存返回NullValue)
*/
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; // 异常降级,放行到数据库
}
}
/**
* 缓存删除(支持单Key和批量删除)
*/
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());
}
}
/**
* 延迟双删(解决Redis主从同步延迟导致的脏读)
*/
public void delayDoubleRemove(String key) {
// 立即删除
remove(key);
// 延迟500毫秒再次删除(适配主从同步延迟)
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)
xml
<!-- Spring Data Redis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<!-- 排除Lettuce依赖 -->
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 引入Jedis依赖 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>4.4.6</version>
</dependency>
<!-- 序列化依赖(Fastjson2) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.41</version>
</dependency>
(2)Jedis配置类(连接池+RedisTemplate)
java
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 {
/**
* Jedis连接池配置(工业级参数)
*/
@Bean
public JedisPoolConfig jedisPoolConfig() {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxTotal(16); // 最大连接数(与Lettuce保持一致)
poolConfig.setMaxIdle(8); // 最大空闲连接数
poolConfig.setMinIdle(4); // 最小空闲连接数
poolConfig.setMaxWaitMillis(-1); // 最大等待时间(-1表示无限制)
poolConfig.setTestOnBorrow(true); // 借出连接时测试可用性(避免使用无效连接)
poolConfig.setTestOnReturn(true); // 归还连接时测试可用性
return poolConfig;
}
/**
* Jedis连接工厂(集群/单机适配)
*/
@Bean
public RedisConnectionFactory redisConnectionFactory(JedisPoolConfig poolConfig) {
JedisConnectionFactory factory = new JedisConnectionFactory(poolConfig);
// 单机配置(集群配置需替换为RedisClusterConfiguration)
factory.setHostName("192.168.1.100");
factory.setPort(6379);
factory.setPassword("your-redis-password");
factory.setTimeout(3000); // 超时时间
factory.setDatabase(0);
return factory;
}
/**
* RedisTemplate配置(与Lettuce共用序列化逻辑)
*/
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory connectionFactory) {
RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(connectionFactory);
// Key序列化:StringRedisSerializer
StringRedisSerializer keySerializer = new StringRedisSerializer();
redisTemplate.setKeySerializer(keySerializer);
redisTemplate.setHashKeySerializer(keySerializer);
// Value序列化:Fastjson2(复用Lettuce的序列化器)
RedisLettuceConfig.Fastjson2RedisSerializer valueSerializer = new RedisLettuceConfig.Fastjson2RedisSerializer(Object.class);
redisTemplate.setValueSerializer(valueSerializer);
redisTemplate.setHashValueSerializer(valueSerializer);
redisTemplate.afterPropertiesSet();
return redisTemplate;
}
}
(3)Jedis业务层封装(与Lettuce API对齐)
java
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* Redis Jedis客户端业务层(备用方案,旧项目兼容)
*/
@Service
public class RedisJedisCacheService {
@Resource
private RedisTemplate<String, Object> redisTemplate;
// 以下方法与RedisLettuceCacheService完全一致,API对齐,便于切换
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)
xml
<!-- Redisson Spring Boot Starter(自动整合Spring) -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.23.5</version>
</dependency>
<!-- 序列化依赖(Fastjson2,与Redis客户端保持一致) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.41</version>
</dependency>
(2)application.yml配置(集群/单机兼容)
yaml
spring:
redis:
password: your-redis-password
timeout: 3000ms
# Redisson配置(独立配置,更灵活)
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管理)
java
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() {
// 加载配置(支持字符串、文件、URL多种方式)
Config config = Config.fromYAML(redissonConfig);
// 创建RedissonClient实例(全局唯一,线程安全)
return Redisson.create(config);
}
}
4.3 Redisson缓存与高级功能实战
(1)Redisson基础缓存实现(分布式Map)
java
import org.redisson.api.RMap;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* Redisson缓存业务层(基础缓存+高级功能)
*/
@Service
public class RedissonCacheService {
@Resource
private RedissonClient redissonClient;
/**
* 获取分布式Map(缓存容器)
*/
private <K, V> RMap<K, V> getCacheMap() {
// 缓存Key前缀(与Redis客户端保持一致,避免Key冲突)
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);
// 设置过期时间(Key级过期)
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核心高级功能:分布式锁(解决缓存击穿)
java
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* Redisson分布式锁工具类(工业级实现,支持红锁)
*/
@Component
public class RedissonLockUtil {
@Resource
private RedissonClient redissonClient;
/**
* 获取分布式锁(非阻塞,失败返回false)
* @param lockKey 锁Key
* @return 锁实例(释放锁需调用unlock())
*/
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;
// 获取锁实例(支持红锁:redissonClient.getRedLock(locks))
RLock lock = redissonClient.getLock(key);
try {
// 尝试获取锁:最多等待waitTime秒,持有leaseTime秒后自动释放(避免死锁)
boolean locked = lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
return locked ? lock : null;
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
return null;
}
}
/**
* 释放锁(必须在finally中调用)
*/
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核心高级功能:延迟队列(优化延迟双删)
java
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;
/**
* Redisson延迟队列工具类(替代线程池,更稳定)
*/
@Component
public class RedissonDelayQueueUtil {
@Resource
private RedissonClient redissonClient;
// 延迟队列名称(统一管理)
private static final String DELAY_QUEUE_NAME = "distributed:delay:queue";
/**
* 添加延迟任务(如延迟双删、订单超时取消)
* @param task 任务内容(需序列化)
* @param delay 延迟时间
* @param timeUnit 时间单位
*/
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;
// 省略getter/setter/构造方法
}
/**
* 延迟队列初始化(应用启动时执行)
*/
@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)实战代码(商品查询示例)
java
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; // 数据库DAO(MyBatis/MyBatis-Plus)
/**
* 商品查询(高并发全链路缓存逻辑)
*/
public ProductDTO getProductById(Long productId) {
if (productId == null || productId <= 0) {
return null;
}
String cacheKey = "product:" + productId;
ProductDTO productDTO;
// 1. 查询本地缓存(Caffeine)
productDTO = (ProductDTO) localCacheService.get(cacheKey);
if (productDTO != null) {
System.out.printf("本地缓存命中:key=%s%n", cacheKey);
return productDTO;
}
// 2. 查询Redis缓存(Lettuce)
productDTO = (ProductDTO) lettuceCacheService.get(cacheKey);
if (productDTO != null) {
// 同步到本地缓存
localCacheService.put(cacheKey, productDTO);
System.out.printf("Redis缓存命中:key=%s%n", cacheKey);
return productDTO;
}
// 3. 缓存未命中,加分布式锁防击穿(Redisson)
String lockKey = "product:lock:" + productId;
RedissonLockUtil.RLock lock = redissonLockUtil.tryLock(lockKey);
if (lock == null) {
// 未获取到锁,返回默认值或降级处理
return null;
}
try {
// 4. 再次查询Redis(避免锁等待期间已被其他线程写入)
productDTO = (ProductDTO) lettuceCacheService.get(cacheKey);
if (productDTO != null) {
localCacheService.put(cacheKey, productDTO);
return productDTO;
}
// 5. 查询数据库
productDTO = productDAO.selectById(productId);
if (productDTO != null) {
// 热点数据延长过期时间(3小时)
long expireSeconds = CacheConstants.DISTRIBUTED_CACHE_HOT_EXPIRE_SECONDS;
// 回写Redis+本地缓存
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 {
// 1. 先更新数据库
boolean success = productDAO.update(productDTO);
if (!success) {
return false;
}
// 2. 删除本地缓存
localCacheService.remove(cacheKey);
// 3. 删除Redis缓存+延迟双删
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)实战代码(简化版商品查询)
java
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;
// 1. 本地缓存查询
productDTO = (ProductDTO) localCacheService.get(cacheKey);
if (productDTO != null) {
return productDTO;
}
// 2. Redisson缓存查询
productDTO = (ProductDTO) redissonCacheService.get(cacheKey);
if (productDTO != null) {
localCacheService.put(cacheKey, productDTO);
return productDTO;
}
// 3. 分布式锁防击穿
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;
}
// 4. 数据库查询+回写缓存
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,避免冷启动穿透。