搭建Caffeine+Redis多級緩存機制

本地緩存的簡單實現方案有HashMap,CucurrentHashMap,成熟的本地緩存方案有Guava 與 Caffeine ,企業級應用推薦下面說下兩者的區別

1. 核心異同對比

特性Guava CacheCaffeine
誕生背景Google Guava 庫的一部分(2011年)基于 Guava Cache 重構的現代緩存庫(2015+)
性能中等(鎖競爭較多)極高(優化并發設計,吞吐量提升5~10倍)
內存管理基于 LRU 算法結合?W-TinyLFU?算法(高命中率)
過期策略支持 expireAfterWrite/access支持 expireAfterWrite/access +?refresh
緩存回收同步阻塞異步非阻塞(后臺線程)
監控統計基礎統計(命中率等)詳細統計(命中率、加載時間等)
依賴需引入整個 Guava 庫輕量(僅依賴 Caffeine)
社區維護維護模式(新功能少)活躍更新(Java 17+ 兼容)

從上面的比較可知,?Caffeine 各方面是優于Guava的,因此在搭建多級緩存機制時,建議使用Caffeine+Redis的組合方案。

業務執行流程

  1. 請求優先讀取?Caffeine 本地緩存(超快,減少網絡IO)。

  2. 本地緩存未命中 → 讀取?Redis 分布式緩存

  3. Redis 未命中 → 查詢數據庫,并回填到兩級緩存。

下面介紹下實現方式

注意:下面的實現方式是基于Springboot 2.4+,版本不同,配置上會略有差異

1.maven中引入下面的依賴

<!-- Caffeine 本地緩存 -->
<dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId>
</dependency><!-- 緩存抽象層 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId>
</dependency><!-- redis 緩存操作 -->
<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>
</dependency>

?2.application中進行配置

spring: cache:caffeine:spec: maximumSize=1000,expireAfterWrite=10m  # 本地緩存redis:time-to-live: 1h  # Redis緩存過期時間# redis 配置redis:# 地址host: 127.0.0.1# 端口,默認為6379port: 6379# 數據庫索引database: 0# 密碼password: abc123# 連接超時時間timeout: 6000ms  # 連接超時時長(毫秒)jedis:pool:max-active: 1000  # 連接池最大連接數(使用負值表示沒有限制)max-wait: -1ms      # 連接池最大阻塞等待時間(使用負值表示沒有限制)max-idle: 10      # 連接池中的最大空閑連接min-idle: 5       # 連接池中的最小空閑連接

?3.自定義多級緩存管理器


import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.cache.support.CompositeCacheManager;
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 org.springframework.data.redis.serializer.StringRedisSerializer;
import com.github.benmanes.caffeine.cache.Caffeine;import java.util.concurrent.TimeUnit;@Configuration
@EnableCaching
@EnableConfigurationProperties(CacheProperties.class)
public class MyCacheConfig {@Beanpublic RedisCacheConfiguration cacheConfiguration(CacheProperties cacheProperties) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig();config = config.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()));config = config.serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));CacheProperties.Redis redisProperties = cacheProperties.getRedis();if (redisProperties.getTimeToLive() != null) {config = config.entryTtl(redisProperties.getTimeToLive());}if (redisProperties.getKeyPrefix() != null) {config = config.prefixKeysWith(redisProperties.getKeyPrefix());}if (!redisProperties.isCacheNullValues()) {config = config.disableCachingNullValues();}if (!redisProperties.isUseKeyPrefix()) {config = config.disableKeyPrefix();}return config;}@Beanpublic Caffeine<Object, Object> caffeineConfig() {return Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(10, TimeUnit.MINUTES);}@Bean@Primary  // 添加 @Primary 注解指定 CaffeineCacheManager 作為默認的緩存管理器public CacheManager caffeineCacheManager(Caffeine<Object, Object> caffeine) {CaffeineCacheManager manager = new CaffeineCacheManager();manager.setCaffeine(caffeine);return manager;}@Beanpublic RedisCacheManager redisCacheManager(RedisConnectionFactory redisConnectionFactory,RedisCacheConfiguration cacheConfiguration) {return RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(cacheConfiguration).build();}@Beanpublic CacheManager compositeCacheManager(@Qualifier("caffeineCacheManager") CacheManager caffeineCacheManager,@Qualifier("redisCacheManager") CacheManager redisCacheManager) {return new CompositeCacheManager(caffeineCacheManager,redisCacheManager);}
}

4.業務邏輯層調用

使用示例:

@Service
public class ProductService {@Autowiredprivate ProductRepository repository;// 優先讀本地緩存,其次Redis,最后數據庫@Cacheable(cacheNames = "product", key = "#id")public Product getProductById(Long id) {return repository.findById(id).orElseThrow();}// 更新數據時清除兩級緩存@CacheEvict(cacheNames = "product", key = "#product.id")public Product updateProduct(Product product) {return repository.save(product);}
}

手動控制多級緩存

@Service
public class CacheService {@Autowiredprivate CacheManager cacheManager;@Autowiredprivate RedisTemplate<String, Object> redisTemplate;public Product getProductWithManualControl(Long id) {// 1. 先查本地緩存Cache caffeineCache = cacheManager.getCache("product");Product product = caffeineCache.get(id, Product.class);if (product != null) {return product;}// 2. 查Redis緩存product = (Product) redisTemplate.opsForValue().get("product:" + id);if (product != null) {// 回填本地緩存caffeineCache.put(id, product);return product;}// 3. 查數據庫product = repository.findById(id).orElseThrow();// 回填兩級緩存redisTemplate.opsForValue().set("product:" + id, product, Duration.ofHours(1));caffeineCache.put(id, product);return product;}
}
  1. 緩存一致性

    • 使用?@CacheEvict?或?Redis Pub/Sub?同步失效兩級緩存。

    • 示例:通過 Redis 消息通知其他節點清理本地緩存。

  2. 防止緩存擊穿

    • Caffeine 配置?refreshAfterWrite

Caffeine.newBuilder().refreshAfterWrite(5, TimeUnit.MINUTES).build(key -> loadFromRedisOrDb(key));

3.監控統計:

Caffeine 統計:cache.getNativeCache().stats()

Redis 統計:INFO commandstats

4. 驗證多級緩存
本地緩存生效:連續調用同一接口,觀察第二次響應時間驟降。
Redis 緩存生效:重啟應用后,首次請求仍快速返回(數據來自Redis)。

?

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/79874.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/79874.shtml
英文地址,請注明出處:http://en.pswp.cn/web/79874.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【Linux系統】第四節—詳解yum+vim

hello 我是云邊有個稻草人 Linux—本節課所屬專欄—歡迎訂閱—持續更新中~ 目錄 畫板—本節課知識點詳解 一、軟件包管理器 1.1 什么是軟件包 1.2 Linux軟件?態 1.3 yum具體操作 【查看軟件包】 【安裝軟件】 【卸載軟件】 【注意事項】 1.4 安裝源 二、vim 2.1 …

EasyRTC嵌入式音視頻通信SDK打造帶屏IPC全場景實時通信解決方案

一、方案概述? 在智能安防與物聯網快速發展的背景下&#xff0c;帶屏IPC&#xff08;網絡攝像機&#xff09;不僅承擔著視頻采集與監控的基礎功能&#xff0c;還逐漸向多樣化交互與智能化方向演進。EasyRTC作為一款強大的實時通信框架&#xff0c;具備低延遲、高穩定性、跨平…

Linux下的c/c++開發之操作Redis數據庫

C/C 操作 Redis 的常用庫 在 C/C 開發中操作 Redis 有多種方式&#xff0c;最主流的選擇是使用第三方客戶端庫。由于 Redis 官方本身是使用 C 編寫的&#xff0c;提供的 API 非常適合 C/C 調用。常見的 Redis C/C 客戶端庫包括&#xff1a; hiredis&#xff1a;官方推薦的輕量…

go 通過匯編學習atomic原子操作原理

文章目錄 概要一、原理1.1、案例1.2、關鍵匯編 二、LOCK匯編指令2.1、 LOCK2.2、 原理2.2.1、 緩存行2.2.2、 緩存一致性之MESI協議2.2.3、lock原理 三、x86緩存發展四、x86 DMA發展參考 概要 在并發操作下&#xff0c;對一個簡單的aa2的操作都會出錯&#xff0c;這是因為這樣…

mapreduce打包運行

maven打包 MapReduce是一個分布式運算程序的編程框架&#xff0c;是用戶開發“基于Hadoop的數據分析應用”的核心框架。 MapReduce核心功能是將用戶編寫的業務邏輯代碼和自帶默認組件整合成一個完整的分布式運算程序&#xff08;例如&#xff1a;jar包&#xff09;&#xff0…

小白成長之路-LInux系統文件與目錄管理(二)

提示&#xff1a;第二部分對第一部分收尾 文章目錄 常見的命令如下一、文件查看命令1. more命令2.less命令3.head命令4.tail命令5.nl命令&#xff08;了解&#xff09;6.創建目錄命令7.創建文件命令>: 覆蓋重定向>>: 追加重定向 8.touch命令9.echo命令10.文件或目錄復…

JVM之虛擬機運行

虛擬機運行快速復習 try-catch&#xff1a;catch-異常表棧展開&#xff0c;finally-代碼復制異常表兜底 類的生命周期&#xff1a;加載&#xff0c;連接&#xff08;驗證&#xff0c;準備&#xff0c;解析&#xff09;&#xff0c;初始化&#xff0c;使用&#xff0c;卸載 類…

AI數字人實現原理

隨著人工智能與數字技術的快速發展&#xff0c;AI數字人&#xff08;Digital Human&#xff09;作為新一代人機交互媒介&#xff0c;正在多個行業中快速落地。無論是在虛擬主播、在線客服、教育培訓&#xff0c;還是在數字代言、元宇宙中&#xff0c;AI數字人都扮演著越來越重要…

Android開發-數據庫SQLite

在Android應用開發中&#xff0c;當需要存儲結構化數據時&#xff0c;SQLite是一個非常強大的工具。SQLite是一款輕量級的關系型數據庫管理系統&#xff0c;它內嵌于Android系統中&#xff0c;支持SQL語法&#xff0c;并且不需要單獨的服務器進程或系統配置。本文將介紹如何在A…

android實現USB通訊

在 Android 上枚舉 USB 設備除了使用 UsbManager.getDeviceList() 方法外&#xff0c;還有以下幾種常見的方式&#xff1a; 1. 使用 USB 設備過濾器&#xff08;XML 配置&#xff09; 通過在 AndroidManifest.xml 中配置 USB 設備過濾器&#xff0c;可以讓系統自動檢測并通知…

FFmpeg視頻編碼的完整操作指南

步驟如下&#xff1a; 安裝和準備FFmpeg&#xff1a;確保包含所需編碼器&#xff08;如libx264&#xff09;。基本命令行編碼&#xff1a;使用ffmpeg命令進行轉碼&#xff0c;設置視頻編碼器、CRF、預設等。API編碼流程&#xff08;針對開發者&#xff09;&#xff1a; a. 注冊…

鴻蒙 UIAbility組件與UI的數據同步和窗口關閉

使用 EventHub 進行數據通信 Stage模型概念圖 根據 Stage 模型概念圖 UIAbility 先于 ArkUI Page 創建 所以&#xff0c;事件要先 .on 訂閱 再 emit 發布 假如現在有頁面 Page1 和他的 UIAbility // src/main/ets/page1ability/Page1Ability.ets onCreate(want: Want, laun…

全棧工程師實戰手冊:LuatOS日志系統開發指南!

本文聚焦LuatOS-log庫的實戰應用場景&#xff0c;通過完整案例演示日志模塊集成、格式定制及遠程同步方案&#xff0c;幫助全棧開發者構建靈活可靠的日志管理框架。下面&#xff0c;我們一起來認識LuatOS的log庫&#xff01; 一、 log.info() log info()主要打印一些正常的…

STM32-USART串口通信(9)

一、通信接口介紹 通信的目的&#xff1a;將一個設備的數據傳送到另一個設備&#xff0c;擴展硬件系統。 當STM32想要實現一些功能&#xff0c;但是需要外掛一些其他模塊才能實現&#xff0c;這就需要在兩個設備之間連接上一根或多跟通信線&#xff0c;通過通信線路發送或者接…

【MoveIt 2】使用 MoveIt 任務構造器(MoveIt Task Constructor)進行拾取和放置

本教程將引導您創建一個使用 MoveIt 任務構造器規劃抓取和放置操作的包。MoveIt 任務構造器&#xff08;https://github.com/moveit/moveit_task_constructor/tree/ros2/&#xff09;提供了一種為包含多個不同子任務&#xff08;稱為階段&#xff09;的任務進行規劃的方法。如果…

破解商業綜合體清潔管理困局:商業空間AI智能保潔管理系統全場景解決方案

方案整體概述 隨著商業綜合體日益向智能化、精細化管理轉型&#xff0c;傳統保潔工作面臨人員監管難、清潔效果評估難、應急響應滯后等諸多挑戰。為解決這些痛點&#xff0c;本系統依托計算機視覺、行為識別、圖像分割與深度學習等AI技術&#xff0c;構建一套集人員管理、工作…

spring響應式編程系列:異步消費數據

目錄 示例 大致流程 parallel cache PARALLEL_SUPPLIER newParallel init publishOn new MonoSubscribeOnValue ???????subscribe ???????new LambdaMonoSubscriber ???????MonoSubscribeOnValue.subscribe ???????onSubscribe ??…

視頻編解碼學習十二之Android疑點

一、android.view.SurfaceControl.setDisplaySurface的作用 android.view.SurfaceControl.setDisplaySurface 是 Android 系統中一個 native 層級別的 API&#xff0c;主要用于 設置某個物理顯示屏&#xff08;Display&#xff09;的輸出 Surface&#xff0c;屬于 SurfaceFlin…

家用或辦公 Windows 電腦玩人工智能開源項目配備核顯的必要性(含 NPU 及顯卡類型補充)

一、GPU 與顯卡的概念澄清 首先需要明確一個容易誤解的概念&#xff1a;GPU 不等同于顯卡。 顯卡和GPU是兩個不同的概念。 【概念區分】 在討論圖形計算領域時&#xff0c;需首先澄清一個常見誤區&#xff1a;GPU&#xff08;圖形處理單元&#xff09;與顯卡&#xff08;視…

Python----神經網絡(《Deep Residual Learning for Image Recognition》論文和ResNet網絡結構)

一、論文 1.1、論文基本信息 標題&#xff1a;Deep Residual Learning for Image Recognition 作者&#xff1a;Kaiming He, Xiangyu Zhang, Shaoqing Ren, Jian Sun 單位&#xff1a;Microsoft Research 會議&#xff1a;CVPR 2016 主要貢獻&#xff1a;提出了一種深度殘…