18人参与 • 2026-01-25 • Redis
在高并发系统中,缓存是提升响应速度、减轻数据库压力的核心手段,但单一缓存方案往往难以应对复杂场景 —— 本地缓存缺乏分布式一致性,redis 缓存存在网络开销,还可能遭遇穿透、雪崩、击穿等致命问题。本文基于实战案例,详解 springboot 整合caffeine 本地缓存 + redis 分布式缓存 + 空值缓存的三级缓存方案,从架构设计到代码落地,构建高可用、低延迟的缓存体系。
传统缓存方案要么依赖单一本地缓存(无法分布式共享),要么仅用 redis(网络 io 开销影响性能),而三级缓存架构通过 “本地缓存 + 分布式缓存 + 数据库” 的层级设计,实现了 “速度” 与 “一致性” 的平衡:
问题:恶意请求查询不存在的数据(如 id=-1 的商品),导致缓存失效后直接穿透到数据库,引发性能问题。解决方案:空值缓存 + 布隆过滤器双重防护
问题:大量缓存数据在同一时间过期,或 redis 集群宕机,导致所有请求瞬间涌向数据库,引发数据库雪崩。解决方案:随机过期时间 + 优雅降级
问题:热点数据(如秒杀商品)缓存过期瞬间,大量并发请求穿透到数据库,导致数据库压力骤增。解决方案:热点数据预热 + 分布式锁
首先引入核心依赖(maven 示例),包含 springboot 缓存 starter、caffeine、redis、redisson(分布式锁):
<!-- springboot缓存核心依赖 -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-cache</artifactid>
</dependency>
<!-- caffeine本地缓存 -->
<dependency>
<groupid>com.github.ben-manes.caffeine</groupid>
<artifactid>caffeine</artifactid>
<version>3.1.8</version>
</dependency>
<!-- redis依赖 -->
<dependency>
<groupid>org.springframework.boot</groupid>
<artifactid>spring-boot-starter-data-redis</artifactid>
</dependency>
<!-- redisson分布式锁 -->
<dependency>
<groupid>org.redisson</groupid>
<artifactid>redisson-spring-boot-starter</artifactid>
<version>3.23.3</version>
</dependency>
配置 caffeine 缓存参数、redis 连接信息、分布式锁等:
spring:
# redis配置
redis:
host: 127.0.0.1
port: 6379
password: 123456
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 2
max-wait: 1000ms
timeout: 3000ms
# 缓存配置
cache:
type: caffeine
caffeine:
# 初始容量、最大容量、过期时间(写入后30分钟过期)
initial-capacity: 100
maximum-size: 1000
expire-after-write: 30m
# 自定义缓存配置
cache:
# 空值缓存过期时间(5分钟)
null-value-expire: 5m
# 热点数据预热key前缀
hot-data-prefix: "hot:"
# 分布式锁前缀
lock-prefix: "cache:lock:"
import com.github.benmanes.caffeine.cache.caffeine;
import org.springframework.cache.cachemanager;
import org.springframework.cache.caffeine.caffeinecachemanager;
import org.springframework.context.annotation.bean;
import org.springframework.context.annotation.configuration;
import org.springframework.data.redis.cache.rediscacheconfiguration;
import org.springframework.data.redis.cache.rediscachemanager;
import org.springframework.data.redis.connection.redisconnectionfactory;
import org.springframework.data.redis.serializer.genericjackson2jsonredisserializer;
import org.springframework.data.redis.serializer.redisserializationcontext;
import org.springframework.data.redis.serializer.stringredisserializer;
import java.time.duration;
import java.util.hashmap;
import java.util.map;
@configuration
public class cacheconfig {
// caffeine缓存管理器(本地缓存)
@bean
public cachemanager caffeinecachemanager() {
caffeinecachemanager cachemanager = new caffeinecachemanager();
// 配置caffeine缓存参数:初始容量100,最大容量1000,写入后30分钟过期
cachemanager.setcaffeine(caffeine.newbuilder()
.initialcapacity(100)
.maximumsize(1000)
.expireafterwrite(duration.ofminutes(30)));
return cachemanager;
}
// redis缓存管理器(分布式缓存)
@bean
public rediscachemanager rediscachemanager(redisconnectionfactory connectionfactory) {
// 序列化配置(避免redis存储乱码)
rediscacheconfiguration config = rediscacheconfiguration.defaultcacheconfig()
.entryttl(duration.ofminutes(30)) // 默认过期时间30分钟
.serializekeyswith(redisserializationcontext.serializationpair
.fromserializer(new stringredisserializer()))
.serializevalueswith(redisserializationcontext.serializationpair
.fromserializer(new genericjackson2jsonredisserializer()));
// 自定义不同缓存的过期时间(如空值缓存5分钟)
map<string, rediscacheconfiguration> cacheconfigs = new hashmap<>();
cacheconfigs.put("nullvaluecache", config.entryttl(duration.ofminutes(5)));
return rediscachemanager.builder(connectionfactory)
.cachedefaults(config)
.withinitialcacheconfigurations(cacheconfigs)
.build();
}
}
核心逻辑:先查 caffeine→再查 redis→最后查数据库,同时处理空值缓存、分布式锁、缓存更新:
import org.redisson.api.rlock;
import org.redisson.api.redissonclient;
import org.springframework.cache.cache;
import org.springframework.cache.cachemanager;
import org.springframework.data.redis.core.redistemplate;
import org.springframework.stereotype.component;
import javax.annotation.resource;
import java.util.concurrent.timeunit;
@component
public class cacheutil {
@resource
private cachemanager caffeinecachemanager;
@resource
private rediscachemanager rediscachemanager;
@resource
private redistemplate<string, object> redistemplate;
@resource
private redissonclient redissonclient;
@resource
private bloomfilterutil bloomfilterutil; // 布隆过滤器工具类
// 缓存查询核心方法:key-缓存键,clazz-返回类型,dbloader-数据库查询逻辑
public <t> t getcache(string key, class<t> clazz, dataloader<t> dbloader) {
// 1. 布隆过滤器校验:无效key直接返回null
if (!bloomfilterutil.contains(key)) {
return null;
}
// 2. 查询caffeine本地缓存
cache caffeinecache = caffeinecachemanager.getcache("localcache");
t localvalue = caffeinecache.get(key, clazz);
if (localvalue != null) {
return localvalue;
}
// 3. 查询redis分布式缓存
cache rediscache = rediscachemanager.getcache("rediscache");
t redisvalue = rediscache.get(key, clazz);
if (redisvalue != null) {
// redis命中,同步到本地缓存
caffeinecache.put(key, redisvalue);
return redisvalue;
}
// 4. 缓存未命中,分布式锁控制数据库查询
rlock lock = redissonclient.getlock("cache:lock:" + key);
try {
// 尝试获取锁,最多等待3秒,持有锁10秒
if (lock.trylock(3, 10, timeunit.seconds)) {
// 再次查询redis(防止其他线程已更新缓存)
redisvalue = rediscache.get(key, clazz);
if (redisvalue != null) {
caffeinecache.put(key, redisvalue);
return redisvalue;
}
// 5. 查询数据库
t dbvalue = dbloader.load();
if (dbvalue != null) {
// 数据库有结果,更新各级缓存
rediscache.put(key, dbvalue);
caffeinecache.put(key, dbvalue);
} else {
// 数据库无结果,缓存空值(5分钟过期)
cache nullvaluecache = rediscachemanager.getcache("nullvaluecache");
nullvaluecache.put(key, null);
caffeinecache.put(key, null);
}
return dbvalue;
} else {
// 获取锁失败,返回默认值或抛出异常
throw new runtimeexception("缓存更新繁忙,请稍后重试");
}
} catch (interruptedexception e) {
thread.currentthread().interrupt();
return null;
} finally {
// 释放锁
if (lock.isheldbycurrentthread()) {
lock.unlock();
}
}
}
// 数据加载函数式接口(封装数据库查询逻辑)
@functionalinterface
public interface dataloader<t> {
t load();
}
}
当数据库数据发生变更(新增、修改、删除)时,需同步清除各级缓存,避免数据不一致:
// 缓存清除方法(用于数据库更新后)
public void clearcache(string key) {
// 1. 清除本地缓存
cache caffeinecache = caffeinecachemanager.getcache("localcache");
caffeinecache.evict(key);
// 2. 清除redis缓存
cache rediscache = rediscachemanager.getcache("rediscache");
rediscache.evict(key);
// 3. 清除空值缓存
cache nullvaluecache = rediscachemanager.getcache("nullvaluecache");
nullvaluecache.evict(key);
}
通过commandlinerunner实现系统启动时预热热点数据,避免缓存冷启动:
import org.springframework.boot.commandlinerunner;
import org.springframework.stereotype.component;
import javax.annotation.resource;
import java.util.list;
@component
public class hotdatapreloader implements commandlinerunner {
@resource
private cacheutil cacheutil;
@resource
private productmapper productmapper; // 数据库dao层
@override
public void run(string... args) throws exception {
// 加载热点商品数据(如销量前100的商品)
list<product> hotproducts = productmapper.selecthotproducts(100);
for (product product : hotproducts) {
string key = "product:" + product.getid();
// 存入本地缓存和redis
cacheutil.caffeinecachemanager.getcache("localcache").put(key, product);
cacheutil.rediscachemanager.getcache("rediscache").put(key, product);
}
system.out.println("热点数据预热完成,共加载" + hotproducts.size() + "条数据");
}
}
springboot+caffeine+redis + 空值缓存的三级缓存方案,通过 “本地缓存提效、分布式缓存保一致、空值缓存防穿透” 的设计,完美解决了高并发场景下的缓存核心难题。该方案不仅能将接口响应时间压缩至毫秒级,还能大幅降低数据库压力,同时具备故障隔离、优雅降级的高可用特性,适用于电商、支付、社交等各类高并发系统。
实际落地时,可根据业务场景灵活调整缓存参数(如过期时间、最大容量)和预热策略,结合监控工具持续优化,让缓存体系真正成为系统的 “性能加速器”。
到此这篇关于caffeine结合redis空值缓存实现多级缓存的文章就介绍到这了,更多相关caffeine结合redis实现多级缓存内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
您想发表意见!!点此发布评论
版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。
发表评论