SpringBoot+Caffeine+Redis實(shí)現(xiàn)多級(jí)緩存的方法
多級(jí)緩存:架構(gòu)設(shè)計(jì)、實(shí)戰(zhàn)落地與問題解決
在高并發(fā)分布式系統(tǒng)中,緩存是提升接口響應(yīng)速度、降低數(shù)據(jù)庫(kù)壓力的核心技術(shù),但單一緩存層往往難以兼顧性能、一致性和高可用。多級(jí)緩存通過整合不同層級(jí)的緩存組件,揚(yáng)長(zhǎng)避短構(gòu)建層次化緩存體系,成為支撐百萬(wàn)級(jí) QPS 系統(tǒng)的標(biāo)配方案。本文將從核心概念、架構(gòu)設(shè)計(jì)、實(shí)戰(zhàn)實(shí)現(xiàn)、一致性保障及經(jīng)典問題解決五個(gè)維度,全面解析多級(jí)緩存技術(shù),同時(shí)結(jié)合 SpringBoot+Caffeine+Redis 的實(shí)戰(zhàn)案例,讓技術(shù)落地更具可操作性。
一、什么是多級(jí)緩存?
多級(jí)緩存是指在系統(tǒng)中部署多層功能互補(bǔ)的緩存組件,讓請(qǐng)求按照預(yù)設(shè)順序依次訪問各層緩存,僅當(dāng)所有緩存層均未命中時(shí),才訪問底層數(shù)據(jù)庫(kù)的架構(gòu)模式。其核心設(shè)計(jì)思想是離用戶越近的緩存,速度越快、開銷越低,通過分層攔截請(qǐng)求,最大化減少對(duì)后端存儲(chǔ)的訪問。
在 Java 后端體系中,最主流的是二級(jí)緩存架構(gòu)(本地緩存 + 分布式緩存),部分場(chǎng)景會(huì)結(jié)合數(shù)據(jù)庫(kù)自身緩存形成三級(jí)體系,各層核心特性與選型如下:
- 本地緩存:運(yùn)行在應(yīng)用進(jìn)程內(nèi)部的內(nèi)存緩存,無(wú)網(wǎng)絡(luò) IO 開銷,讀寫延遲納秒級(jí)。主流選型為 Caffeine(性能優(yōu)于 Guava Cache、Ehcache),適合存儲(chǔ)高頻訪問的熱點(diǎn)數(shù)據(jù),缺點(diǎn)是數(shù)據(jù)僅當(dāng)前實(shí)例可見,無(wú)法分布式共享,且受 JVM 內(nèi)存限制。
- 分布式緩存:獨(dú)立于應(yīng)用的中間件,部署在集群中可被所有應(yīng)用實(shí)例共享,數(shù)據(jù)一致性更易保障。主流選型為 Redis(支持豐富數(shù)據(jù)結(jié)構(gòu)、持久化、分布式鎖),適合存儲(chǔ)全量熱點(diǎn)數(shù)據(jù) + 通用業(yè)務(wù)數(shù)據(jù),缺點(diǎn)是存在網(wǎng)絡(luò) IO 開銷,性能略低于本地緩存。
- 數(shù)據(jù)庫(kù)緩存:數(shù)據(jù)庫(kù)自身的查詢緩存 / 緩沖池(如 MySQL 的 InnoDB Buffer Pool),屬于底層被動(dòng)緩存,通常無(wú)需人工干預(yù),僅作為最后一道數(shù)據(jù)屏障。
各層緩存性能對(duì)比(核心指標(biāo)):
| 緩存層級(jí) | 讀寫延遲 | 集群共享 | 部署成本 | 適用場(chǎng)景 |
|---|---|---|---|---|
| 本地緩存(Caffeine) | 納秒級(jí) | 否 | 低(無(wú)獨(dú)立組件) | 高頻熱點(diǎn)數(shù)據(jù)、單機(jī)維度臨時(shí)數(shù)據(jù) |
| 分布式緩存(Redis) | 微秒級(jí) | 是 | 中(需集群部署) | 全量熱點(diǎn)數(shù)據(jù)、跨實(shí)例共享數(shù)據(jù) |
| 數(shù)據(jù)庫(kù)緩存 | 毫秒級(jí) | 是 | 高 | 所有數(shù)據(jù)的最終存儲(chǔ) |
二、為什么需要多級(jí)緩存?
單一緩存層在高并發(fā)場(chǎng)景下存在難以克服的短板,而多級(jí)緩存通過優(yōu)勢(shì)互補(bǔ)解決這些問題,同時(shí)帶來(lái)極致的性能提升。我們通過一組電商商品詳情頁(yè)的壓測(cè)數(shù)據(jù),直觀感受多級(jí)緩存的價(jià)值:
| 架構(gòu)模式 | QPS | TP99 延遲 (ms) | 數(shù)據(jù)庫(kù)負(fù)載 | 緩存命中率 |
|---|---|---|---|---|
| 單 Redis 緩存 | 5.8 萬(wàn) | 120 | 65% | 75% |
| Caffeine+Redis 多級(jí)緩存 | 120 萬(wàn) | 45 | 8% | 99.2% |
從數(shù)據(jù)可以看出,多級(jí)緩存讓 QPS 提升 20 倍、TP99 延遲降低 60%、數(shù)據(jù)庫(kù)負(fù)載減少 87%,核心價(jià)值體現(xiàn)在三個(gè)方面:
- 極致性能:本地緩存攔截 80% 以上的高頻請(qǐng)求,避免大量請(qǐng)求穿透到 Redis,減少網(wǎng)絡(luò) IO 和 Redis 集群壓力,讓接口響應(yīng)速度達(dá)到極致。
- 高可用兜底:當(dāng)分布式緩存集群(如 Redis)發(fā)生宕機(jī)時(shí),本地緩存可臨時(shí)兜底核心熱點(diǎn)數(shù)據(jù),避免系統(tǒng)直接雪崩,提升服務(wù)容錯(cuò)能力。
- 資源優(yōu)化:通過分層存儲(chǔ)數(shù)據(jù),將高頻數(shù)據(jù)放在本地緩存(低開銷),通用數(shù)據(jù)放在分布式緩存(共享性),避免 Redis 存儲(chǔ)大量低價(jià)值數(shù)據(jù),降低緩存集群的部署成本。
此外,多級(jí)緩存還能從架構(gòu)層面規(guī)避單一緩存的經(jīng)典問題,比如本地緩存的分布式一致性問題可通過 Redis 兜底,Redis 的網(wǎng)絡(luò)開銷問題可通過本地緩存攔截,讓系統(tǒng)更健壯。
三、多級(jí)緩存核心架構(gòu)設(shè)計(jì)
3.1 核心設(shè)計(jì)原則
多級(jí)緩存的設(shè)計(jì)需遵循3 個(gè)核心原則,否則易導(dǎo)致架構(gòu)臃腫、數(shù)據(jù)一致性混亂:
- 請(qǐng)求就近原則:請(qǐng)求優(yōu)先訪問本地緩存,未命中再訪問分布式緩存,最后訪問數(shù)據(jù)庫(kù),反向流程不可行。
- 數(shù)據(jù)分層原則:不同價(jià)值的數(shù)據(jù)存儲(chǔ)在對(duì)應(yīng)層級(jí),本地緩存僅存高頻熱點(diǎn)數(shù)據(jù)(控制容量,避免 OOM),分布式緩存存全量業(yè)務(wù)數(shù)據(jù),不重復(fù)存儲(chǔ)低價(jià)值數(shù)據(jù)。
- 失效由上至下原則:緩存更新 / 失效時(shí),先操作本地緩存,再操作分布式緩存,避免出現(xiàn) “本地緩存有舊數(shù)據(jù),分布式緩存有新數(shù)據(jù)” 的不一致情況。
3.2 主流架構(gòu):Caffeine+Redis 二級(jí)緩存
Java 后端中,Caffeine(本地)+ Redis(分布式) 是工業(yè)級(jí)落地的主流架構(gòu),適用于 90% 以上的業(yè)務(wù)場(chǎng)景(電商、社交、資訊等),其請(qǐng)求訪問流程和數(shù)據(jù)更新流程如下,是后續(xù)實(shí)戰(zhàn)的核心基礎(chǔ)。
(1)請(qǐng)求訪問流程(讀操作)
- 應(yīng)用接收到請(qǐng)求后,首先查詢Caffeine 本地緩存,命中則直接返回結(jié)果,結(jié)束請(qǐng)求;
- 本地緩存未命中,查詢Redis 分布式緩存,命中則將數(shù)據(jù)回寫到本地緩存(方便后續(xù)請(qǐng)求攔截),然后返回結(jié)果;
- 分布式緩存也未命中,執(zhí)行數(shù)據(jù)庫(kù)查詢,查詢結(jié)果非空時(shí),依次回寫到 Redis 和 Caffeine,然后返回結(jié)果;
- 數(shù)據(jù)庫(kù)查詢結(jié)果為空時(shí),執(zhí)行空值緩存(短期過期),避免后續(xù)請(qǐng)求穿透到數(shù)據(jù)庫(kù)。
(2)數(shù)據(jù)更新流程(寫操作)
遵循 “先更數(shù)據(jù)庫(kù),再刪緩存” 原則(避免 “先刪緩存,再更數(shù)據(jù)庫(kù)” 的并發(fā)不一致問題),核心步驟:
- 執(zhí)行數(shù)據(jù)庫(kù)的增 / 刪 / 改操作,保證數(shù)據(jù)落地;
- 刪除 Redis 對(duì)應(yīng)緩存(而非更新,避免并發(fā)覆蓋);
- 驅(qū)逐 Caffeine 本地緩存(當(dāng)前實(shí)例),若為集群部署,通過消息隊(duì)列(如 RocketMQ/Kafka)通知其他實(shí)例驅(qū)逐本地緩存;
- 后續(xù)請(qǐng)求會(huì)觸發(fā)緩存重新加載,保證數(shù)據(jù)最新。
注意:寫操作優(yōu)先選擇刪緩存而非更緩存,原因是:若同時(shí)有多個(gè)寫請(qǐng)求,直接更新緩存可能導(dǎo)致并發(fā)覆蓋,而刪除緩存后,由讀請(qǐng)求觸發(fā)懶加載,能保證緩存數(shù)據(jù)的最終一致性。
四、實(shí)戰(zhàn)落地:SpringBoot 整合 Caffeine+Redis 多級(jí)緩存
本節(jié)基于SpringBoot 2.7.x,實(shí)現(xiàn) Caffeine+Redis 的二級(jí)緩存架構(gòu),包含核心依賴、配置、代碼實(shí)現(xiàn),同時(shí)整合空值緩存、分布式鎖等特性,直接可用于生產(chǎn)環(huán)境(僅需根據(jù)業(yè)務(wù)調(diào)整參數(shù))。
4.1 核心依賴引入(Maven)
引入 Caffeine、Redis、SpringCache 核心依賴,簡(jiǎn)化緩存操作,無(wú)需手動(dòng)封裝緩存工具類:
<!-- SpringCache緩存抽象(簡(jiǎn)化緩存注解使用) -->
<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分布式緩存(Lettuce客戶端) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- 序列化依賴(解決Redis存儲(chǔ)對(duì)象亂碼問題) -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson2</artifactId>
<version>2.0.32</version>
</dependency>4.2 核心配置(配置類 + yml)
(1)緩存配置類:初始化 Caffeine 和 Redis 緩存管理器
通過配置類實(shí)現(xiàn)雙緩存管理器,分別管理本地緩存和分布式緩存,支持注解式緩存操作,核心參數(shù)按需調(diào)整(如過期時(shí)間、最大容量):
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.context.annotation.Primary;
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 java.util.concurrent.TimeUnit;
@Configuration
@EnableCaching // 開啟SpringCache注解支持
public class MultiLevelCacheConfig {
// 1. 本地緩存管理器(Caffeine):優(yōu)先使用,存儲(chǔ)熱點(diǎn)數(shù)據(jù)
@Bean("caffeineCacheManager")
public CacheManager caffeineCacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
// 核心配置:最大容量1000條、寫入后5秒過期(短過期防臟數(shù)據(jù))、弱引用回收
cacheManager.setCaffeine(Caffeine.newBuilder()
.maximumSize(1000)
.expireAfterWrite(5, TimeUnit.SECONDS)
.weakValues());
return cacheManager;
}
// 2. 分布式緩存管理器(Redis):@Primary表示默認(rèn)緩存管理器,存儲(chǔ)通用數(shù)據(jù)
@Bean("redisCacheManager")
@Primary
public CacheManager redisCacheManager(RedisConnectionFactory factory) {
// 序列化配置:解決對(duì)象亂碼
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()))
.entryTtl(30, TimeUnit.SECONDS); // 寫入后30秒過期
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
}(2)yml 配置:Redis 連接信息
spring:
redis:
host: 127.0.0.1
port: 6379
password: 123456
lettuce:
pool:
max-active: 8 # 最大連接數(shù)
max-idle: 8 # 最大空閑連接
min-idle: 2 # 最小空閑連接
max-wait: 1000ms # 連接等待時(shí)間
timeout: 3000ms # 連接超時(shí)時(shí)間4.3 業(yè)務(wù)層實(shí)現(xiàn):注解式多級(jí)緩存操作
基于 SpringCache 注解,實(shí)現(xiàn)商品詳情查詢和商品庫(kù)存更新的核心業(yè)務(wù),完美貼合前文的 “請(qǐng)求訪問流程” 和 “數(shù)據(jù)更新流程”,代碼簡(jiǎn)潔易維護(hù):
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CachePut;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.Optional;
@Service
public class ProductService {
@Resource
private ProductMapper productMapper; // 數(shù)據(jù)庫(kù)Mapper
@Resource
private StringRedisTemplate redisTemplate;
// 空值標(biāo)記:解決緩存穿透
private static final Product NULL_PRODUCT = new Product(-1L, "NULL", 0, 0.0);
/**
* 商品詳情查詢:多級(jí)緩存(先Caffeine,再Redis,最后DB)
* @Cacheable:緩存查詢結(jié)果,指定本地緩存管理器
*/
@Cacheable(value = "product", key = "#id", cacheManager = "caffeineCacheManager")
public Product getProduct(Long id) {
// 1. 本地緩存未命中,查詢Redis
String redisKey = "product:" + id;
Product redisProduct = redisTemplate.opsForValue().get(redisKey);
if (redisProduct != null) {
return redisProduct;
}
// 2. Redis未命中,查詢數(shù)據(jù)庫(kù)
Product dbProduct = productMapper.selectById(id);
// 3. 空值緩存:防止穿透
Product cacheProduct = Optional.ofNullable(dbProduct).orElse(NULL_PRODUCT);
// 4. 回寫Redis:設(shè)置60秒過期
redisTemplate.opsForValue().set(redisKey, cacheProduct, 60, TimeUnit.SECONDS);
// 5. 若為NULL_PRODUCT,返回null,避免業(yè)務(wù)層處理異常
return cacheProduct.equals(NULL_PRODUCT) ? null : cacheProduct;
}
/**
* 商品庫(kù)存更新:先更DB,再刪緩存(保證一致性)
* @CacheEvict:刪除指定緩存
*/
@CacheEvict(value = "product", key = "#productId", cacheManager = "caffeineCacheManager")
public void updateProductStock(Long productId, int delta) {
// 1. 更新數(shù)據(jù)庫(kù):先落地?cái)?shù)據(jù)
productMapper.updateStock(productId, delta);
// 2. 刪除Redis緩存:避免舊數(shù)據(jù)
String redisKey = "product:" + productId;
redisTemplate.delete(redisKey);
// 3. 本地緩存已通過@CacheEvict刪除,無(wú)需手動(dòng)操作
}
}4.4 集群化改造:本地緩存一致性
上述代碼僅適用于單機(jī)部署,集群部署時(shí),某一實(shí)例更新數(shù)據(jù)后,其他實(shí)例的本地緩存仍會(huì)存在舊數(shù)據(jù),導(dǎo)致數(shù)據(jù)不一致。解決該問題的主流方案是基于消息隊(duì)列的緩存通知,核心思路:
- 部署消息隊(duì)列(如 RocketMQ/Kafka),創(chuàng)建緩存更新主題;
- 當(dāng)某一實(shí)例執(zhí)行緩存刪除操作時(shí),向主題發(fā)送緩存失效消息(包含緩存 key、緩存名稱);
- 所有應(yīng)用實(shí)例均作為消費(fèi)者,監(jiān)聽該主題,收到消息后,刪除本地對(duì)應(yīng)緩存;
- 保證消息的至少一次消費(fèi),避免緩存失效消息丟失。
簡(jiǎn)化實(shí)現(xiàn):使用 Redis 的 Pub/Sub 替代消息隊(duì)列(輕量、無(wú)需獨(dú)立部署),實(shí)現(xiàn)緩存通知,適合中小型集群。
五、多級(jí)緩存的一致性保障
多級(jí)緩存的核心痛點(diǎn)是數(shù)據(jù)一致性—— 各層緩存之間、各實(shí)例的本地緩存之間,可能因更新延遲、緩存污染導(dǎo)致數(shù)據(jù)不一致。由于強(qiáng)一致性會(huì)引入分布式事務(wù)、鎖等機(jī)制,導(dǎo)致性能大幅下降,工業(yè)級(jí)場(chǎng)景中均采用最終一致性(允許短時(shí)間不一致,最終所有緩存層數(shù)據(jù)收斂),核心保障策略分為三類:
5.1 緩存更新策略:選對(duì)策略,從源頭減少不一致
主流的緩存更新策略有 3 種,結(jié)合多級(jí)緩存的特性, “先更 DB,再刪緩存” 是最優(yōu)選擇,其他策略僅適用于特殊場(chǎng)景:
- 先更 DB,再刪緩存(推薦):無(wú)并發(fā)覆蓋問題,實(shí)現(xiàn)簡(jiǎn)單,僅存在 “刪緩存失敗” 的小概率問題,可通過定時(shí)任務(wù)補(bǔ)償解決;
- 先刪緩存,再更 DB:高并發(fā)下會(huì)出現(xiàn) “緩存臟數(shù)據(jù)”(A 刪緩存→B 查緩存未命中→查舊 DB→回寫緩存→A 更 DB),導(dǎo)致緩存數(shù)據(jù)長(zhǎng)期不一致;
- 雙寫策略(更新 DB 同時(shí)更新緩存):高并發(fā)下會(huì)出現(xiàn) “緩存覆蓋”(A 和 B 同時(shí)更新 DB,再先后更新緩存,導(dǎo)致緩存數(shù)據(jù)與最新 DB 數(shù)據(jù)不一致),僅適用于低并發(fā)場(chǎng)景。
5.2 過期時(shí)間策略:強(qiáng)制讓臟數(shù)據(jù)失效
為各層緩存設(shè)置合理的過期時(shí)間(TTL) ,是最終一致性的最后一道屏障,即使出現(xiàn)緩存更新失敗,臟數(shù)據(jù)也會(huì)在過期后自動(dòng)失效,重新從 DB 加載最新數(shù)據(jù)。
- 本地緩存(Caffeine):設(shè)置短 TTL(5-10 秒),快速淘汰臟數(shù)據(jù),避免本地緩存長(zhǎng)期不一致;
- 分布式緩存(Redis):設(shè)置中等 TTL(30-60 秒),兼顧緩存命中率和數(shù)據(jù)新鮮度;
- 空值緩存:設(shè)置極短 TTL(3-5 分鐘),避免無(wú)效空值占用過多緩存空間。
同時(shí),為 Redis 緩存設(shè)置隨機(jī) TTL 偏移量(如 30 秒 ±5 秒),避免大量緩存同時(shí)過期導(dǎo)致的雪崩問題。
5.3 異步同步策略:解決集群本地緩存一致性
如前文所述,集群部署時(shí)的本地緩存一致性,通過 “消息通知 + 異步刪除” 實(shí)現(xiàn),主流方案對(duì)比:
| 同步方案 | 實(shí)現(xiàn)難度 | 性能 | 適用場(chǎng)景 |
|---|---|---|---|
| Redis Pub/Sub | 低 | 高 | 中小型集群、對(duì)一致性要求一般的場(chǎng)景 |
| 消息隊(duì)列(RocketMQ/Kafka) | 中 | 高 | 大型集群、核心業(yè)務(wù)場(chǎng)景 |
| 分布式配置中心(Nacos/Apollo) | 中 | 中 | 配置類緩存、低頻更新數(shù)據(jù) |
六、多級(jí)緩存經(jīng)典問題解決
緩存系統(tǒng)的三大經(jīng)典問題 ——穿透、擊穿、雪崩,在多級(jí)緩存架構(gòu)中可通過分層防護(hù)實(shí)現(xiàn)更高效的解決,相比單一緩存層,防護(hù)手段更豐富、效果更徹底。
6.1 緩存穿透:攔截?zé)o效請(qǐng)求,避免穿透到 DB
問題定義:請(qǐng)求查詢的數(shù)在所有緩存層和 DB 中均不存在,導(dǎo)致每次請(qǐng)求都穿透到 DB,若遭遇惡意請(qǐng)求(如偽造不存在的商品 ID),會(huì)壓垮數(shù)據(jù)庫(kù)。多級(jí)緩存防護(hù)方案(雙重防護(hù),層層攔截):
- 第一層:接口層參數(shù)校驗(yàn):在 Controller/Gateway 層對(duì)請(qǐng)求參數(shù)做強(qiáng)校驗(yàn)(如 ID 必須大于 0、符合業(yè)務(wù)規(guī)則),直接攔截明顯無(wú)效的請(qǐng)求;
- 第二層:空值緩存:在 Caffeine 和 Redis 中緩存空值(如前文的 NULL_PRODUCT),設(shè)置短 TTL,讓后續(xù)相同無(wú)效請(qǐng)求被緩存攔截;
- 進(jìn)階方案:布隆過濾器:將 DB 中所有有效主鍵(如商品 ID、用戶 ID)預(yù)先加載到布隆過濾器,請(qǐng)求先經(jīng)過過濾器校驗(yàn),不存在的鍵直接攔截,僅可能存在的鍵才進(jìn)入緩存層,適合超大規(guī)模數(shù)據(jù)場(chǎng)景(如千萬(wàn)級(jí)商品庫(kù))。
6.2 緩存擊穿:保護(hù)熱點(diǎn)數(shù)據(jù),避免單點(diǎn)穿透
問題定義:某個(gè)超高訪問量的熱點(diǎn)數(shù)據(jù)(如秒殺商品)在緩存中過期的瞬間,大量并發(fā)請(qǐng)求同時(shí)穿透到 DB,導(dǎo)致數(shù)據(jù)庫(kù)單點(diǎn)壓力驟增。多級(jí)緩存防護(hù)方案(熱點(diǎn)數(shù)據(jù)專屬防護(hù)):
- 第一層:熱點(diǎn)數(shù)據(jù)永不過期:對(duì)秒殺、首頁(yè)焦點(diǎn)圖等核心熱點(diǎn)數(shù)據(jù),設(shè)置物理永不過期,通過手動(dòng)更新 / 刪除控制緩存生命周期,從源頭避免過期;
- 第二層:本地鎖 + 分布式鎖雙重鎖:緩存過期后,通過本地鎖(synchronized) 攔截單機(jī)內(nèi)的并發(fā)請(qǐng)求,再通過Redis 分布式鎖(Redisson) 攔截集群內(nèi)的并發(fā)請(qǐng)求,僅允許一個(gè)線程查詢 DB 并更新緩存,其他線程等待緩存更新后再查詢;
- 第三層:熱點(diǎn)數(shù)據(jù)預(yù)熱:系統(tǒng)啟動(dòng)時(shí) / 流量高峰前,通過定時(shí)任務(wù)將熱點(diǎn)數(shù)據(jù)主動(dòng)加載到 Caffeine 和 Redis 中,避免緩存冷啟動(dòng)導(dǎo)致的擊穿。
6.3 緩存雪崩:分散過期時(shí)間,避免集體穿透
問題定義:大量緩存數(shù)據(jù)在同一時(shí)間點(diǎn)過期,或分布式緩存集群(Redis)宕機(jī),導(dǎo)致所有請(qǐng)求瞬間涌向 DB,引發(fā)數(shù)據(jù)庫(kù)雪崩,進(jìn)而導(dǎo)致整個(gè)系統(tǒng)癱瘓。多級(jí)緩存防護(hù)方案(架構(gòu)級(jí)防護(hù),容錯(cuò)性拉滿):
- 第一層:差異化 TTL:為 Redis 緩存設(shè)置隨機(jī)過期時(shí)間偏移量(如基礎(chǔ) TTL30 秒 + 隨機(jī) 0-5 秒),讓緩存過期時(shí)間分散,避免集體失效;
- 第二層:多級(jí)緩存兜底:即使 Redis 集群宕機(jī),Caffeine 本地緩存仍能攔截大部分熱點(diǎn)請(qǐng)求,保證核心業(yè)務(wù)可用,同時(shí)觸發(fā)告警機(jī)制;
- 第三層:緩存高可用 + 服務(wù)降級(jí):Redis 采用 Cluster/Sentinel 集群部署,避免單點(diǎn)故障;同時(shí)結(jié)合 Hystrix/Sentinel 實(shí)現(xiàn)服務(wù)降級(jí),當(dāng) DB 壓力達(dá)到閾值時(shí),直接返回本地緩存的兜底數(shù)據(jù),放棄部分?jǐn)?shù)據(jù)新鮮度,保證服務(wù)可用性;
- 第四層:數(shù)據(jù)庫(kù)限流:在 DB 代理層(如 MyCat)設(shè)置訪問限流,控制每秒訪問 DB 的請(qǐng)求數(shù),避免數(shù)據(jù)庫(kù)被壓垮。
七、多級(jí)緩存調(diào)優(yōu)與監(jiān)控
7.1 性能調(diào)優(yōu)關(guān)鍵參數(shù)
多級(jí)緩存的性能調(diào)優(yōu)核心是平衡緩存命中率和資源占用,關(guān)鍵參數(shù)調(diào)整原則:
- Caffeine 調(diào)優(yōu):
maximumSize根據(jù) JVM 內(nèi)存合理設(shè)置(如單機(jī) 1G 內(nèi)存設(shè)置 1000-2000 條),避免 OOM;優(yōu)先使用expireAfterWrite(寫入后過期)而非expireAfterAccess(訪問后過期),減少性能開銷; - Redis 調(diào)優(yōu):開啟持久化(RDB+AOF 混合),避免數(shù)據(jù)丟失;優(yōu)化連接池參數(shù)(如
max-active根據(jù)并發(fā)量設(shè)置),減少連接等待;使用 Redis Cluster 集群,提升并發(fā)能力; - JVM 調(diào)優(yōu):開啟 G1GC,設(shè)置
-XX:MaxGCPauseMillis=100,減少 GC 停頓對(duì)本地緩存的影響;合理設(shè)置 JVM 堆內(nèi)存,為本地緩存預(yù)留足夠空間。
7.2 核心監(jiān)控指標(biāo)
無(wú)監(jiān)控不架構(gòu),多級(jí)緩存需要監(jiān)控各層緩存的核心指標(biāo),及時(shí)發(fā)現(xiàn)緩存失效、命中率低等問題,主流監(jiān)控方案為 Prometheus+Grafana:
- 緩存命中率:核心指標(biāo),本地緩存(Caffeine)命中率應(yīng)≥90%,Redis 命中率應(yīng)≥95%,命中率過低需分析數(shù)據(jù)分布,調(diào)整緩存策略;
- 緩存未命中率:持續(xù)偏高需排查是否存在穿透、擊穿問題;
- Redis 指標(biāo):CPU 使用率、內(nèi)存使用率、網(wǎng)絡(luò) IO、連接數(shù),避免 Redis 成為性能瓶頸;
- 數(shù)據(jù)庫(kù)指標(biāo):QPS、連接數(shù)、慢查詢數(shù),驗(yàn)證緩存攔截效果,若數(shù)據(jù)庫(kù) QPS 持續(xù)偏高,需優(yōu)化緩存策略。
監(jiān)控實(shí)現(xiàn):Caffeine 自帶監(jiān)控指標(biāo),可通過Cache.stats()獲??;Redis 可通過 Exporter 采集指標(biāo);最終在 Grafana 中制作可視化大盤,設(shè)置指標(biāo)告警(如命中率低于 90% 時(shí)觸發(fā)短信 / 釘釘告警)。
八、大廠落地最佳實(shí)踐
多級(jí)緩存在阿里、京東、拼多多等大廠的高并發(fā)場(chǎng)景中已廣泛落地,總結(jié)其核心落地經(jīng)驗(yàn),讓技術(shù)落地更貼合生產(chǎn)環(huán)境:
- 數(shù)據(jù)分層存儲(chǔ):本地緩存僅存TOP 10% 的高頻熱點(diǎn)數(shù)據(jù),其余數(shù)據(jù)存在 Redis,避免本地緩存占用過多 JVM 內(nèi)存;
- 避免過度設(shè)計(jì):中小型系統(tǒng)優(yōu)先使用 “Caffeine+Redis” 二級(jí)架構(gòu),無(wú)需引入更多緩存層,增加架構(gòu)復(fù)雜度;
- 緩存更新失敗補(bǔ)償:針對(duì) “刪緩存失敗” 問題,增加定時(shí)任務(wù)補(bǔ)償(如每分鐘掃描 DB 更新日志,對(duì)比緩存數(shù)據(jù),刪除不一致的緩存);
- 冷啟動(dòng)防護(hù):系統(tǒng)重啟時(shí),先通過預(yù)熱腳本加載熱點(diǎn)數(shù)據(jù),再對(duì)外提供服務(wù),避免冷啟動(dòng)導(dǎo)致的緩存穿透、擊穿;
- 灰度發(fā)布:緩存策略變更(如 TTL 調(diào)整、數(shù)據(jù)分層變更)時(shí),采用灰度發(fā)布,逐步擴(kuò)大范圍,避免全量發(fā)布導(dǎo)致的性能問題。
九、總結(jié)
多級(jí)緩存并非簡(jiǎn)單的 “本地緩存 + 分布式緩存” 組合,而是一套兼顧性能、一致性、高可用的層次化架構(gòu)設(shè)計(jì)思想。其核心價(jià)值在于通過分層攔截請(qǐng)求,將性能做到極致,同時(shí)通過最終一致性策略、分層防護(hù)手段,解決緩存的經(jīng)典問題,成為支撐百萬(wàn)級(jí) QPS 高并發(fā)系統(tǒng)的核心技術(shù)。
本文結(jié)合 SpringBoot+Caffeine+Redis 的實(shí)戰(zhàn)案例,從架構(gòu)設(shè)計(jì)到代碼實(shí)現(xiàn),從一致性保障到問題解決,實(shí)現(xiàn)了技術(shù)的全鏈路落地。在實(shí)際項(xiàng)目中,無(wú)需生搬硬套,可根據(jù)業(yè)務(wù)規(guī)模、并發(fā)量、數(shù)據(jù)一致性要求,靈活調(diào)整緩存架構(gòu)和策略 —— 中小型系統(tǒng)優(yōu)先保證落地簡(jiǎn)單,大型高并發(fā)系統(tǒng)重點(diǎn)做好緩存分層、監(jiān)控和容錯(cuò)。
緩存的本質(zhì)是用空間換時(shí)間,而多級(jí)緩存則是讓這份 “交換” 的性價(jià)比達(dá)到最高,這也是其能成為高并發(fā)系統(tǒng)標(biāo)配的根本原因。
到此這篇關(guān)于SpringBoot+Caffeine+Redis實(shí)現(xiàn)多級(jí)緩存的文章就介紹到這了,更多相關(guān)SpringBoot Caffeine Redis多級(jí)緩存內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
利用Java實(shí)現(xiàn)為Word文檔中的文本與段落添加邊框
在日常的 Java 應(yīng)用開發(fā)中,我們經(jīng)常需要與各種文檔格式打交道,本文將介紹如何使用 Java 為 Word 文檔中的指定文本或段落添加邊框,需要的可以了解下2025-10-10
Java中在時(shí)間戳計(jì)算的過程中遇到的數(shù)據(jù)溢出問題解決
這篇文章主要介紹了Java中在時(shí)間戳計(jì)算的過程中遇到的數(shù)據(jù)溢出問題解決,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-06-06
解決springboot與springcloud版本兼容問題(附版本兼容表)
在基于spring boot搭建spring cloud時(shí),創(chuàng)建eureka后啟動(dòng)服務(wù)發(fā)生報(bào)錯(cuò),本文給大家介紹了解決springboot與springcloud版本兼容問題的幾種方案,需要的朋友可以參考下2024-02-02
java socket實(shí)現(xiàn)聊天室 java實(shí)現(xiàn)多人聊天功能
這篇文章主要為大家詳細(xì)介紹了java socket實(shí)現(xiàn)聊天室,java實(shí)現(xiàn)多人聊天功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-07-07
Java反轉(zhuǎn)數(shù)組輸出實(shí)例代碼
這篇文章主要給大家介紹了關(guān)于Java反轉(zhuǎn)數(shù)組輸出以及利用Java實(shí)現(xiàn)字符串逆序輸出的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
Spring Security6中@PostAuthorize注解的具體使用
@PostAuthorize是Spring Security提供的方法級(jí)安全注解,用于在方法執(zhí)行后根據(jù)返回結(jié)果進(jìn)行權(quán)限校驗(yàn),本文就來(lái)詳細(xì)的介紹一下 @PostAuthorize注解的使用,感興趣的可以了解一下2025-10-10

