ConcurrentHashMapRedis實現二級緩存

1. 為什么使用ConcurrentHashMap?

在Java中,ConcurrentHashMap 是一個線程安全且高效的哈希表實現,廣泛用于高并發場景。將其用作一級緩存的原因主要包括以下幾點:

1.1. 線程安全性
  • ConcurrentHashMap 是線程安全的,支持多個線程同時進行讀寫操作而不會出現數據不一致或競態條件問題。這使其非常適合用作多線程環境下的緩存,因為緩存通常會被多個線程并發訪問。
  • 傳統的 Hashtable 也是線程安全的,但它使用全局鎖,性能較低。而 ConcurrentHashMap 使用分段鎖(Segment)機制,將鎖粒度降低,從而在高并發場景下性能更高。

1.2. 高效的并發訪問
  • ConcurrentHashMap 在高并發場景下表現出色,因為它通過分段鎖(Segment)和無鎖操作(如讀操作)最大限度地減少了鎖競爭。
  • 它支持高吞吐量和低延遲,非常適合緩存這種需要快速讀寫的場景。

1.3. 內存使用效率
  • ConcurrentHashMap 在內存使用上非常高效,適合存儲大量緩存數據。
  • 它通過動態調整容量和負載因子,確保內存的高效利用。

1.4. 擴展性
  • ConcurrentHashMap 支持動態擴容,能夠根據實際需求自動調整內部數組的大小,從而適應數據量的動態變化。
  • 這種特性使得它非常適合用作緩存,因為緩存的大小可能會隨著業務需求的變化而動態調整。

1.5. 與緩存策略結合
  • 一級緩存通常用于快速訪問最近或頻繁訪問的數據,而 ConcurrentHashMap 的高效性和線程安全性使其成為實現這一目標的理想選擇。
  • 它可以與其他緩存策略(如基于時間的過期、基于容量的淘汰等)結合使用,進一步提升緩存的性能和靈活性。

1.6. 集成方便
  • ConcurrentHashMap 是 Java 標準庫的一部分,使用簡單且集成方便,無需引入額外的依賴。
  • 它可以與各種緩存框架(如 Ehcache、Caffeine)或自定義緩存實現無縫結合。

1.7. 補充介紹,多線程環境下使用哈希表
  • HashMap:線程不安全,不建議多線程環境使用
  • ConcurrentHashMap:線程安全,但是使用的是分段鎖(Segment)機制
  • Hashtable:線程安全,但使用的是全局鎖,對所有的操作都加鎖,對性能有很大的影響,會導致嚴重的效率問題
  • HashMap實現原理:

  1. put一個對象的時候,先根據對象的hashcode和數組的長度進行求余,通過余數來確定對象放在數組中的哪一個下表
  2. 每個hash桶中存放的是具體對象的鏈表
  3. 初始化的數組長度為16,中間還可能發生擴容,擴容的時候會對當前的表中的元素hash到新的哈希表中
  4. 鏈表的長度大于6的時候,同時數組的長度大于64時,鏈表會轉化為紅黑樹
  • Hashtable實現原理:

  1. 對數組進行全局加鎖,但是實際操作的時候只會針對一個哈希桶,因此會對系統性能有很大影響,多線程環境不建議使用
  • ConcurrentHashMap實現原理:

  1. 對于所操作的特定哈希桶實施加鎖機制,而其余哈希桶則保持解鎖狀態,這意味著其他未鎖定的哈希桶中的數據可以并行地執行讀寫操作
  2. 理論上講,系統支持的并發讀寫線程數量等同于哈希桶的數量,即每個哈希桶都可以獨立地被一個線程訪問而不影響其它桶的操作。
  3. 擴容優化策略包括:
    a. 當檢測到存儲空間不足時,將底層數組容量翻倍。但值得注意的是,在此過程中,并非一次性遷移所有元素至新映射結構中,而是僅遷移當前正在訪問的那個索引位置上的元素。
    b. 此種方式導致在一段時間內存在兩個版本的數據結構共存。
    c. 在執行查詢操作時,需同時對這兩個版本的數據結構進行搜索以確保結果準確性。
    d. 同樣地,當需要刪除條目時,也需要在這兩份數據結構上分別實施刪除動作。
    e. 新增數據項時,則僅向最新擴展后的映射結構中添加。
    f. 該設計采用了一種典型的空間換時間策略,通過犧牲一定的內存開銷來換取更高的并發性能,這正是ConcurrentHashMap能夠在高并發場景下表現優異的原因之一。更具其底層源碼可以發現,在執行put操作的時候會進行加鎖,使用 CAS(Compare-And-Swap)操作和 synchronized 鎖來保證線程安全,但是get操作不會加鎖,它通過 volatile 語義來保證可見性,能夠讀取到最新的數據,它不會阻塞其他線程的并發訪問,所以 ConcurrentHashMap 的設計是在線程安全和性能之間找到平衡點,get 操作的無鎖化設計是其高性能的關鍵之一
    g. 每次調用getput方法時,都會觸發一個過程:將舊映射中對應索引下的元素逐步遷移到新的映射中;只有當遷移完成后,才會從舊映射中移除這些元素。每次調用get、put方法的時候把舊的map中對應的下標中的元素搬運到新的map中,搬運完之后才會刪除
1.8. 總結

ConcurrentHashMap 作為一級緩存的主要原因是其線程安全性、高效的并發訪問能力、內存使用效率以及擴展性。這些特性使其非常適合在高并發場景下快速讀寫數據,從而提高應用性能。

2. 為什么選擇Redis作為二級緩存?

2.1. 高可用性和持久化
  • Redis 提供了多種持久化機制(如 RDB 和 AOF),能夠在服務器重啟后恢復數據,避免緩存數據丟失。
  • ConcurrentHashMap 是內存中的數據結構,數據僅存在于 JVM 內存中,不具備持久化能力。

2.2. 豐富的數據結構
  • Redis 提供了多種數據結構(如字符串、列表、哈希、集合、有序集合等),能夠更靈活地支持復雜的緩存需求。
  • ConcurrentHashMap 僅支持鍵值對的簡單存儲,功能相對單一。

2.3. 分布式支持
  • Redis 是一個分布式數據庫,支持多節點集群,能夠滿足高并發、大規模數據場景下的緩存需求。
  • ConcurrentHashMap 是單機內存數據結構,無法直接支持分布式場景。

2.4. 4. 高性能
  • Redis 的單線程模型通過事件驅動和非阻塞 IO 實現了高性能的讀寫操作,特別適合高并發場景。
  • ConcurrentHashMap 是基于 CAS 和分段鎖實現的,雖然性能很高,但在高并發場景下可能會因鎖競爭導致性能下降。

2.5. 緩存穿透、擊穿、失效問題的解決方案
  • Redis 提供了多種機制來解決緩存穿透(如布隆過濾器)、緩存擊穿(如互斥鎖)和緩存失效(如預熱)等問題。
  • ConcurrentHashMap 難以直接解決這些問題,需要額外的邏輯實現。

2.6. 數據共享和一致性
  • Redis 可以作為分布式緩存,支持多個服務實例共享緩存數據,保證數據一致性。
  • ConcurrentHashMap 是單機的,無法實現跨服務實例的數據共享。

2.7. 緩存分層
  • Redis 通常作為二級緩存,而 ConcurrentHashMap 作為一級緩存(本地內存緩存)。這種分層設計能夠優化性能,同時降低內存占用。
  • 本地緩存(一級緩存)負責快速訪問,Redis(二級緩存)負責數據持久化和跨服務共享。

2.8. 支持復雜業務邏輯
  • Redis 提供了豐富的命令和事務支持,能夠直接在緩存層處理一些復雜的業務邏輯。
  • ConcurrentHashMap 僅支持簡單的鍵值操作,無法處理復雜邏輯。

2.9. 總結

Redis 作為二級緩存的優勢在于其高性能、分布式能力、持久化支持和豐富的功能,能夠彌補 ConcurrentHashMap 的不足。通過將 ConcurrentHashMap 作為一級緩存(本地內存緩存),Redis 作為二級緩存(分布式緩存),可以構建一個高效、可靠、可擴展的緩存系統。

3. 使用ConcurrentHashMap和redis實現二級緩存的優點和缺點

3.1. 優點
  1. 性能分層優化
    • ConcurrentHashMap(本地緩存):內存級訪問速度(納秒級),減少高頻熱點數據的重復遠程請求。
    • Redis(遠程緩存):提供跨進程/節點的數據共享,支持高并發讀取,避免直接穿透到數據庫。
  1. 降低數據庫壓力
    • 兩級緩存組合可攔截大多數查詢請求,尤其在突發流量下,本地緩存直接響應請求,減少對Redis和數據庫的負載。
  1. 適應分布式與單機場景
    • 本地緩存:適用于單機高頻熱點數據(如配置信息)。
    • Redis:解決分布式環境下多節點數據一致性問題。
  1. 資源利用優化
    • 本地緩存節省網絡開銷,Redis支持豐富的數據結構(如Hash、SortedSet)和持久化能力。

3.2. 缺點
  1. 數據一致性挑戰
    • 同步延遲:本地緩存更新可能滯后于Redis,尤其是在分布式場景下(如某節點未及時收到失效通知)。
    • 更新策略復雜性:需實現雙重失效機制(如Redis Pub/Sub通知本地緩存失效),增加代碼復雜度。
  1. 資源占用風險
    • 本地內存壓力:ConcurrentHashMap緩存過多數據可能導致JVM內存溢出或頻繁GC。
    • Redis運維成本:需監控內存、持久化策略,集群部署增加運維復雜度。
  1. 緩存異常場景放大
    • 緩存穿透:本地和Redis均未命中時,請求可能直接擊穿到數據庫。
    • 雪崩風險:兩級緩存同時失效可能導致數據庫瞬時壓力激增。
  1. 設計復雜度高
    • 需實現緩存逐級加載(如本地→Redis→DB)、鎖競爭控制(如本地緩存未命中時,避免多個線程重復加載數據)。

3.3. 適用場景建議
  • 推薦使用:讀多寫少的高頻數據(如商品詳情、配置信息),且對一致性要求不苛刻(允許短暫過期)。
  • 避免使用:寫多讀少或強一致性場景(如庫存扣減),本地緩存頻繁失效會抵消性能優勢。

3.4. 優化思路
  1. 一致性保障
    • 通過Redis的Pub/Sub或定時輪詢,主動失效本地緩存。
    • 對本地緩存設置較短TTL,結合寫后更新策略(Write-Through)。
  1. 異常防護
    • 本地緩存使用軟引用(SoftReference)防止內存溢出。
    • Redis層增加分布式鎖或熔斷機制,避免緩存擊穿。
  1. 監控與治理
    • 監控本地緩存命中率、Redis內存使用率。
    • 使用Guava Cache或Caffeine替代ConcurrentHashMap,支持容量限制、權重過期等策略。

通過合理設計,ConcurrentHashMap+Redis二級緩存可顯著提升系統性能,但需在一致性、復雜度、資源消耗之間謹慎權衡。

4. Java Spring項目中的使用

4.1. ConcurrentHashMap一級緩存
package com.project.demo.Admin.cache;import com.project.demo.Admin.model.constant.CacheConstant;
import com.project.demo.Admin.model.response.GetTrendResponse;
import lombok.extern.slf4j.Slf4j;import java.time.Duration;
import java.time.LocalDateTime;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;/*** @className: LocalCache* @author: 顧漂亮* @date: 2025/7/22 11:33*/
@Slf4j
//本地緩存 -- 一級緩存
public class LocalCache {// 提供線程安全的操作,適合多線程環境下的緩存訪問,支持高效的并發讀寫操作private static final ConcurrentHashMap<String, CacheItem> cache = new ConcurrentHashMap<>();// 使用ScheduledExecutorService實現定時清理過期數據,創建了一個單線程的調度執行器 (newScheduledThreadPool(1))private static final ScheduledExecutorService cleaner = Executors.newScheduledThreadPool(1);// 定時任務,每一分鐘清理一次static {cleaner.scheduleAtFixedRate(LocalCache::cleanExpiredCache,0, //等待幾分鐘后開始,此處0代表立即開始CacheConstant.CLEANUP_INTERVAL_MINUTES, //每隔指定分鐘進行一次TimeUnit.MINUTES); // 時間單位,此處以分鐘為單位log.info("清除ConcurrentHashMap中的緩存");}// 緩存項包裝類,記錄存儲時間private static class CacheItem{final GetTrendResponse value;  //存儲的數據類型final LocalDateTime storedTime; //開始存儲的時間//初始化數據CacheItem(GetTrendResponse value) {this.value = value;this.storedTime = LocalDateTime.now();  //獲取當前系統的時間戳}}/*** 獲取緩存* @param key 鍵* @return 值*/public static GetTrendResponse get(String key) {CacheItem item = cache.get(key);if (item != null && !isExpired(item)){return item.value;}return null;}/*** 添加緩存* @param key 鍵* @param value 值*/public static void put(String key, GetTrendResponse value) {cache.put(key, new CacheItem(value));}/*** 清理過期緩存*/private static void cleanExpiredCache() {log.info("開始清理ConcurrentHashMap中過期緩存");int initialSize = cache.size(); // 初始緩存大小cache.entrySet().removeIf(entry -> isExpired(entry.getValue()));int finalSize = cache.size();  // 清理之后緩存大小log.info("清理了{}個緩存項,剩余{}個緩存項", initialSize - finalSize, finalSize);}/*** 判斷緩存是否過期* @param item 鍵* @return true:過期,false:未過期*/private static boolean isExpired(CacheItem item) {//1. 計算從存儲時間到現在的時間差Duration duration = Duration.between(item.storedTime, LocalDateTime.now());//2. 檢查是否超過了緩存有效期return duration.toMinutes() > CacheConstant.CACHE_EXPIRY_MINUTES;}/*** 關閉定時任務,調用 cleaner.shutdown() 來關閉 ScheduledExecutorService,避免內存泄露和資源浪費*/public static void shutdown() {cleaner.shutdown();}
}
4.2. 使用Redis結合Spring Cache進行二級緩存
package com.project.demo.common.config.cache;import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
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;/** @className: RedisConfig* @author: 顧漂亮* @date: 2025/7/29 16:49*//*** Redis緩存配置類* 用于配置Spring Cache與Redis的整合,定義緩存管理器及序列化方式*/
@EnableCaching // 啟動緩存功能
@Configuration
public class RedisConfig {/*** 創建緩存管理器* @param factory Redis連接工廠* @return 緩存管理器*/@Beanpublic RedisCacheManager cacheManager(RedisConnectionFactory factory) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() //獲取Redis緩存的默認配置作為基礎配置.serializeKeysWith(RedisSerializationContext //配置緩存鍵(key)的序列化方式,Key: "users::1".SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext //配置緩存值(value)的序列化方式,Value: {"id":1,"name":"張三"}.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));return RedisCacheManager.builder(factory) //構建Redis緩存管理器.cacheDefaults(config).build();}
}

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

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

相關文章

Mysql集群技術

實驗在RHEL7中做&#xff0c;因為9中缺少了一個關鍵的高可用組件環境&#xff1a;兩臺數據庫&#xff0c;內存和CPU要多一點主流是MYSQL&#xff08;開源&#xff09;&#xff0c;Oracle收費較貴RHEL7中直接用make編譯是有問題的&#xff0c;所以需要要gcc工具做好前置準備&…

自動駕駛嵌入式軟件工程師面試題【持續更新】

文章目錄前言請描述 CAN 幀的基本結構&#xff08;包括標識符、數據字段、CRC 等&#xff09;描述 WebSocket 協議的基本工作流程&#xff08;包括握手、數據幀結構&#xff09;請說明如何實現 WebSocket 連接的心跳機制以檢測連接狀態&#xff0c;并描述在斷開后如何通過重連策…

vue(5)-組件

一.組件三大組成部分&#xff08;結構/樣式/邏輯&#xff09;&#xff08;1&#xff09;組件樣式沖突用scoped全局樣式在組件中起全局作用&#xff0c;局部樣式可以加scoped屬性來只作用于當前組件圖中只給baseone加這個樣式&#xff0c;就在baseone中style加scoped&#xff08…

【機器學習】兩大線性分類算法:邏輯回歸與線性判別分析:找到分界線的藝術

文章目錄一、核心概念&#xff1a;數據分類的"切分線"二、工作原理&#xff1a;從"找分界線"理解二、常見算法1、邏輯回歸&#xff1a;二分類2、線性判別分析&#xff08;LDA&#xff09;&#xff1a;分類與降維3、兩種算法對比分析三、實際應用&#xff1…

靜態分析c/cpp源碼函數調用關系圖生成

calltree calltree 不好使用 Dpxygen https://www.doxygen.nl/download.html Graphviz https://graphviz.org/download/ 靜態代碼調用結構圖分析、構建、生成 doxygen doxygen在win和linux上均可運行&#xff0c;可以自動分析源碼&#xff0c;對c語言項目友好&#xff0c;預處…

使用 MySQL Shell 進行 MySQL 單機到 InnoDB Cluster 的數據遷移實踐

遷移背景與環境原來都是用mysqldump&#xff0c;DTS或者cdc遷移&#xff0c;這次8.0用了下新工具感覺挺好用的&#xff0c;簡單快捷&#xff0c;30G數據不到源環境&#xff1a;單機 MySQL 8.0&#xff0c;地址為 172.23.3.28目標環境&#xff1a;InnoDB Cluster 集群&#xff0…

淘寶商品API可以獲取哪些商品詳情數據?

商品詳情頁商品全部sku信息"skus": {"sku": [{"price": 45.6,"total_price": 0,"orginal_price": 45.6,"properties": "1627207:39617249736","properties_name": "1627207:39617249736…

新一代PLC控制軟件平臺EsDA-AWStudio

在工業自動化和智能制造領域&#xff0c;高效的軟件平臺是提升開發效率和系統性能的關鍵。ZLG致遠電子推出的EsDA-AWStudio平臺&#xff0c;憑借其強大的功能和靈活的設計&#xff0c;為工業控制和物聯網應用提供了全新的解決方案。一站式PLC工業控制軟件平臺EsDA-AWStudioZLG致…

基于深度學習的醫學圖像分析:使用MobileNet實現醫學圖像分類

前言 醫學圖像分析是計算機視覺領域中的一個重要應用&#xff0c;特別是在醫學圖像分類任務中&#xff0c;深度學習技術已經取得了顯著的進展。醫學圖像分類是指將醫學圖像分配到預定義的類別中&#xff0c;這對于疾病的早期診斷和治療具有重要意義。近年來&#xff0c;MobileN…

docker 容器常用命令

在平常的開發工作中&#xff0c;我們經常需要使用 docker 容器&#xff0c;那么常用的 docker 容器命令有哪些呢&#xff1f;今天簡單總結下。 一&#xff1a;查看容器查看運行的容器&#xff1a;docker ps查看所有的容器&#xff1a;docker ps a查看容器詳細信息&#…

重型機械作業誤傷預警響應時間縮短80%!陌訊多模態識別算法在工程現場的應用優化

一、行業痛點&#xff1a;機械作業場景的識別困境據《工程機械安全白皮書&#xff08;2025&#xff09;》統計&#xff0c;施工現場因機械盲區導致的工傷事故中??78.3%由識別延遲引發??。核心難點包括&#xff1a;??動態遮擋問題??&#xff1a;吊臂擺動導致目標部件部分…

2025年ESWA SCI1區TOP,強化學習多目標灰狼算法MOGWO-RL+分布式混合流水車間調度,深度解析+性能實測

目錄1.摘要2.問題描述和數學建模3.強化學習多目標灰狼算法MOGWO-RL4.結果展示5.參考文獻6.算法輔導應用定制讀者交流1.摘要 本文針對大規模個性化制造&#xff08;MPM&#xff09;中的調度問題&#xff0c;提出了一種新的解決方案。MPM能夠在確保大規模生產的前提下&#xff0…

Mac 系統下安裝 nvm

Mac 系統下安裝 nvm nvm 全稱為 node version manger&#xff0c;顧名思義就是管理 node 版本的一個工具&#xff0c;通過這個工具&#xff0c;我們可以在一臺計算機上安裝多個版本的 node&#xff0c;并且隨時進行無縫的切換。 1. 卸載原本的 node.js&#xff08;重要&#xf…

變量篩選—隨機森林特征重要性

對于接觸算法模型不久的小伙伴來說,建模中海量變量篩選總是讓人頭疼,不知道如何把握。之前已經介紹了一些變量篩選的方法:變量篩選一張圖、【變量篩選】計算類別型變量IV值、KS值、一文囊括風控建模中的變量篩選方法、變量篩選—特征包含信息量。本文詳細介紹通過隨機森林算…

【設計模式】 3.設計模式基本原則

單一職責原則 對于一個類而言&#xff0c;有且僅有一個引起他變化的原因或者說&#xff0c;一個類只負責一個職責 如果一個類承擔的職責過多&#xff0c;那么這些職責放在一起耦合度太高了&#xff0c;一個職責的變化可能會影響這個類其他職責的能力。 所以我們在做軟件設計的時…

ABP VNext + Redis Bloom Filter:大規模緩存穿透防護與請求去重

ABP VNext Redis Bloom Filter&#xff1a;大規模緩存穿透防護與請求去重 &#x1f680; &#x1f4da; 目錄ABP VNext Redis Bloom Filter&#xff1a;大規模緩存穿透防護與請求去重 &#x1f680;TL;DR ?1. 引言 &#x1f389;2. 環境與依賴 &#x1f6e0;?3. Bloom Filt…

構建工具和腳手架:從源碼到dist

構建工具和腳手架&#xff1a;從源碼到dist**1. 為什么需要工程轉換&#xff1f;****2. 構建工具的核心職責**為什么要求轉換**1&#xff09;明確三種關鍵問題****&#xff08;2&#xff09;Webpack 的打包機制****3. 開發服務器&#xff08;Webpack Dev Server&#xff09;***…

數字化生產管理系統設計

下面提供一個基于Python的數字化管理系統框架&#xff0c;使用現代技術棧實現。這個系統將包含設備監控、故障管理、裝配配套表生成、生產計劃管理等功能。系統架構數字化生產管理系統 ├── 設備監控模塊 ├── 故障管理模塊 ├── 產品裝配管理模塊 ├── 生產計劃管理模…

【vue】創建響應式數據ref和reactive的區別

目錄 1、所謂響應式數據 2、ref創建基本類型響應式數據 3、reactive創建對象類型響應式數據 4、ref定義對象類型響應式數據 5、總結&#xff1a;ref和reactive對比 6、補充&#xff1a;toRefs與toRef 1、所謂響應式數據 所謂響應式數據就是&#xff0c;在vue頁面中&#…

YOLO12 改進、魔改|直方圖 Transformerm模塊HTB ,通過動態范圍特征分組、針對性注意力與多尺度融合,提高對遮擋以及多尺度目標的關注能力

在惡劣天氣&#xff08;如雨、雪、霧&#xff09;下的圖像恢復任務中&#xff0c;傳統 Transformer 模型為降低計算量&#xff0c;常將自注意力限制在固定空間范圍或僅在通道維度操作&#xff0c;導致難以捕捉長距離空間特征&#xff0c;尤其無法有效處理天氣退化像素&#xff…