Redis之緩存擊穿

Redis之緩存擊穿

在這里插入圖片描述

文章目錄

  • Redis之緩存擊穿
    • 一、什么是緩存擊穿
    • 二、緩存擊穿常見解決方案
      • 1. 互斥鎖(Mutex Lock)
      • 2. 永不過期 + 后臺刷新
      • 3. 邏輯過期(異步更新)
    • 三、案例
      • 1.基于互斥鎖解決緩存擊穿
      • 2.基于邏輯過期解決緩存擊穿
    • 四、注意事項
      • 1.? 鎖的選擇
      • 2. 遞歸重試風險
      • 3. 鎖超時時間
      • ?4. 緩存過期時間隨機化

一、什么是緩存擊穿

緩存擊穿(Cache Breakdown)是指某個熱點 Key 在緩存中過期后,大量并發請求同時繞過緩存直接訪問數據庫,導致數據庫壓力驟增的現象。

通常發生在以下場景:

  • 某個 Key 是高頻訪問的「熱點數據」。
  • Key 的緩存過期時間到期,此時大并發請求同時到達。
  • 緩存失效瞬間,所有請求都去查詢數據庫并重建緩存。
線程1 線程2 線程3 線程4 1.查詢緩存,未命中 2.查詢數據庫,重建緩存數據 3.查詢緩存,未命中 4.查詢數據庫,重建緩存數據 5.查詢緩存,未命中 7.查詢緩存,未命中 6.查詢數據庫,重建緩存數據 數據查詢耗時等待200ms 8.查詢數據庫,重建緩存數據 9.寫入緩存 線程1 線程2 線程3 線程4

二、緩存擊穿常見解決方案

1. 互斥鎖(Mutex Lock)

  • 原理:當緩存失效時,只允許一個線程去加載數據,其他線程等待緩存更新完成后再讀取緩存。
    • 優點: 確保數據強一致性
    • 缺點:線程需要等待,可能成為性能瓶頸(鎖競爭)

  • 流程圖
緩存命中
緩存未命中
成功
失敗
請求緩存
返回數據
獲取互斥鎖
查詢數據庫
更新緩存
釋放鎖
等待并重試
  • 偽代碼
public Object getData(String key) {Object data = cache.get(key);if (data != null) return data;// 加鎖(如Redis的SETNX)String lockKey = "lock:" + key;if (redis.setnx(lockKey, "1", 10)) { // 10秒鎖超時try {// 二次檢查緩存(防止鎖競爭期間其他線程已加載)data = cache.get(key);if (data != null) return data;data = db.query(key);cache.set(key, data);} finally {redis.del(lockKey); // 釋放鎖}} else {// 等待重試Thread.sleep(100);return getData(key);}return data;
}

2. 永不過期 + 后臺刷新

  • 原理:為緩存設置永不過期時間,同時通過后臺線程主動更新緩存。
    • 優點:無阻塞,適合對一致性要求低場景
    • 缺點:數據可能短暫陳舊

  • 流程圖
緩存命中
緩存未命中
請求緩存
返回數據
查詢數據庫并更新緩存
后臺定時任務
  • 偽代碼
// 初始化時設置緩存永不過期
cache.set("hot_key", data)// 后臺線程定期更新
public void backgroundRefresh() {while(true) {Thread.sleep(5 * 60 * 1000)  // 每5分鐘更新一次newData = db.query("hot_key")cache.set("hot_key", newData)}
}

3. 邏輯過期(異步更新)

  • 原理:在緩存中存儲數據的邏輯過期時間,即使緩存未物理過期,若邏輯過期則異步更新。
    • 優點:無阻塞,兼容性強
    • 缺點:不保證一致性,實現復雜度較高

  • 時序圖
線程1 線程2 線程3 線程4 1.查詢緩存 發現邏輯時間已過期 2.獲取互斥鎖成功 3.開啟新線程 (異步操作) 1.查詢緩存 發現邏輯時間已過期 2.獲取互斥鎖失敗 3.返回過期數據 4.返回過期數據 1.查詢數據庫 重建緩存數據 2.寫入緩存 重置邏輯過期時間 1.命中緩存,并且沒有過期 2.返回緩存數據 3.釋放鎖 線程1 線程2 線程3 線程4
  • 偽代碼

緩存條目類

@Data
public class CacheEntry {private final String data;private final long expireTime;
}
    // 獲取當前時間戳(毫秒)private static long now() {return System.currentTimeMillis();}public static String getData(String key) {CacheEntry entry = cache.get(key);// 緩存未命中if (entry == null) {String data = Database.query(key);long expireTime = now() + 300_000; // 5分鐘過期(300秒 * 1000)cache.put(key, new CacheEntry(data, expireTime));return data;}// 檢查邏輯過期if (entry.getExpireTime() < now()) {// 啟動異步更新線程new Thread(() -> asyncUpdate(key)).start();}// 返回過期數據return entry.getData();}private static void asyncUpdate(String key) {String newData = Database.query(key);long newExpireTime = now() + 300_000;cache.put(key, new CacheEntry(newData, newExpireTime));}

三、案例

1.基于互斥鎖解決緩存擊穿

命中
未命中
開始
提交商鋪id
從Redis查詢商鋪緩存
判斷緩存是否命中
返回數據
結束
嘗試獲取互斥鎖
判斷是否獲取鎖
根據id查詢數據庫
休眠一段時間
將商鋪數據寫入Redis
釋放互斥鎖
public Shop queryWithMutex(Long id) {// 1.從redis查詢商鋪緩存String key = CACHE_SHOP_KEY + id;String shopJson = stringRedisTemplate.opsForValue().get(key);// 2.判斷是否存在if (StrUtil.isNotBlank(shopJson)) {// 3.存在,直接返回return JSONUtil.toBean(shopJson, Shop.class);}// 判斷命中的是否是空值if(shopJson != null) {// 返回錯誤信息,解決緩存穿透問題return null;}// 4.實現緩存重建// 4.1 獲取互斥鎖String lockKey = LOCK_SHOP_KEY + id;Shop shop;try {boolean isLock = tryLock(lockKey);// 4.2 判斷是否獲取成功if (!isLock) {// 4.3 失敗,則休眠并重試Thread.sleep(50);return queryWithMutex(id);}// 4.4 成功,根據id查詢數據庫,返回數據shop = getById(id);if (shop == null) {// 5.數據庫不存在,將空字串寫入Redis,設置過期時間,解決緩存穿透問題stringRedisTemplate.opsForValue().set(key, "", CACHE_NULL_TTL, TimeUnit.MINUTES);// 返回錯誤信息,解決緩存穿透問題return null;}// 6.存在,寫入redisstringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);} catch (InterruptedException e) {throw new RuntimeException(e);} finally {// 7.釋放鎖unLock(lockKey);}return shop;}

2.基于邏輯過期解決緩存擊穿

未命中
命中
未過期
過期
開始
提交商鋪id
從Redis查詢商鋪緩存
判斷緩存是否命中
返回空
結束
判斷緩存是否過期
返回商鋪信息
嘗試獲取互斥鎖
判斷是否獲取鎖
開啟獨立線程
根據id查詢數據庫
將商鋪數據寫入Redis,并設置邏輯過期時間
釋放互斥鎖
public Shop queryWithLogicalExpire(Long id) {// 1.從redis查詢商鋪緩存String key = CACHE_SHOP_KEY + id;String shopJson = stringRedisTemplate.opsForValue().get(key);// 2.判斷是否存在if (StrUtil.isBlank(shopJson)) {// 3.不存在,直接返回nullreturn null;}// 4.命中,需要先把json反序列化為對象RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);JSONObject data = (JSONObject) redisData.getData();Shop shop = JSONUtil.toBean(data, Shop.class);LocalDateTime expireTime = redisData.getExpireTime();// 5.判斷是否過期if (expireTime.isAfter(LocalDateTime.now())) {// 5.1 未過期,直接返回數據return shop;}// 5.2 過期,需要緩存重建// 6. 緩存重建String lockKey = LOCK_SHOP_KEY + id;// 6.1 獲取互斥鎖boolean isLock = tryLock(lockKey);// 6.2 判斷是否獲取鎖成功if (isLock) {// 6.3 成功,開啟獨立線程,實現緩存重建CACHE_REBUILD_EXECUTOR.submit(() -> {try {this.saveShop2Redis(id, 20L);} catch (Exception e) {throw new RuntimeException(e);} finally {// 6.5 釋放鎖unLock(lockKey);}});}// 6.4 返回過期的商鋪信息return shop;}

四、注意事項

1.? 鎖的選擇

  • 單機環境用 ReentrantLocksynchronized
  • 分布式環境需用 Redis 分布式鎖(如 Redisson 的 RLock)。

2. 遞歸重試風險

  • 示例中遞歸調用,可能導致棧溢出,實際生產環境應改用循環重試。

3. 鎖超時時間

  • 分布式鎖需設置合理超時時間(如 300ms),防止死鎖。

?4. 緩存過期時間隨機化

  • 可對緩存 TTL 添加隨機值(如 300 + rand.nextInt(100)),避免緩存雪崩。

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

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

相關文章

Spring Boot 中使用 Netty

2025/4/15 向 一、什么是Netty Netty 是 Java 中一個非常高性能的網絡通信框架&#xff0c;用來開發服務器和客戶端程序&#xff0c;主要用于處理 TCP/UDP 的網絡連接&#xff0c;比如&#xff1a; 聊天服務 實時推送 高并發網絡通信&#xff08;比如游戲、IoT、金融系統&a…

【QT】 QT定時器的使用

QT定時器的使用 1. QTimer介紹&#xff08;1&#xff09;QTimer的使用方法步驟示例代碼1&#xff1a;定時器的啟動和關閉現象&#xff1a;示例代碼2&#xff1a;定時器每隔1s在標簽上切換圖片現象&#xff1a; (2)實際開發的作用 2.日期 QDate(1)主要方法 3.時間 QTime(1)主要方…

排序算法詳細介紹對比及備考建議

文章目錄 排序算法對比基本概要 算法逐一介紹1. 冒泡排序&#xff08;Bubble Sort&#xff09;2. 選擇排序&#xff08;Selection Sort&#xff09;3. 插入排序&#xff08;Insertion Sort&#xff09;&#x1f31f;&#x1f31f;4. 希爾排序&#xff08;Shell Sort&#xff09…

Docker華為云創建私人鏡像倉庫

Docker華為云創建私人鏡像倉庫 在華為云官網的 產品 中搜索 容器鏡像服務 &#xff1a; 或者在其他頁面的搜索欄中搜索 容器鏡像服務 &#xff1a; 進入到頁面后&#xff0c;點擊 創建組織 &#xff08;華為云的鏡像倉庫稱為組織&#xff09;&#xff1a; 設置組織名字后&…

微信小程序-自定義toast

微信小程序-自定義toast 微信小程序原生的toast最多能顯示兩行文字。方案1:方案2 微信小程序原生的toast最多能顯示兩行文字。 有時候并不能滿足業務需求。所以我們需要使用第三方或者自定義。 方案1: 第三方vant-toast 微信小程序下載引入第三方vant之后。 在需要使用的頁面…

安卓手游逆向

一、環境安裝 1.1、安裝Java環境 1.2、安裝SDK環境 1.3、安裝NDK環境 二、APK 2.1、文件結構 2.2、打包流程 2.3、安裝流程 應用安裝涉及目錄: system/app ----->系統自帶的應用程序,獲得adb root權限才能刪除。 data/app ------->用戶程序安裝的目錄,安裝…

VSCode Continue 擴展踩坑記錄

Trae 是一款很優秀的 AI 開發工具&#xff0c;但目前支持的平臺還較少&#xff0c;比如不支持 Win7&#xff0c;不支持 Linux&#xff0c;為了在這些平臺上進行開發&#xff0c;我需要尋找一個替代品。經過網上搜索&#xff0c;選擇了 VSCode Continue 擴展&#xff0c;但在使…

Elasticsearch:AI 助理 - 從通才到專才

作者&#xff1a;來自 Elastic Thorben Jndling 在 AI 世界中&#xff0c;關于構建針對特定領域定制的大型語言模型&#xff08;large language models - LLM&#xff09;的話題備受關注 —— 不論是為了更好的安全性、上下文理解、專業能力&#xff0c;還是更高的準確率。這個…

【ARM】MDK燒錄提示Error:failed to execute‘ ‘

1、 文檔目標 解決在燒錄程序的時候&#xff0c;因為選擇了錯誤的燒錄方式導致下載失敗的情況。 2、 問題場景 在燒錄程序的時候出現了提示&#xff1a;“Error&#xff1a;failed to execute ’ ”&#xff08;如圖2-1&#xff09;。檢測Target->Debug配置發現沒有問題&a…

系統分析師(六)-- 計算機網絡

概述 TCP/IP 協議族 DNS DHCP 網絡規劃與設計 邏輯網絡設計 物理網絡設計 題目 層次化網絡設計 網絡冗余設計 綜合布線系統 IP地址 網絡接入技術 其他網絡技術應用 物聯網

優化運營、降低成本、提高服務質量的智慧物流開源了

智慧物流視頻監控平臺是一款功能強大且簡單易用的實時算法視頻監控系統。它的愿景是最底層打通各大芯片廠商相互間的壁壘&#xff0c;省去繁瑣重復的適配流程&#xff0c;實現芯片、算法、應用的全流程組合&#xff0c;從而大大減少企業級應用約95%的開發成本可通過邊緣計算技術…

從One-Hot到TF-IDF:NLP詞向量演進解析與業務實戰指南(一)

從One-Hot到TF-IDF&#xff1a;詞向量演進之路 開場白&#xff1a; 想象一下&#xff0c;你試圖用Excel表格分析《紅樓夢》的情感傾向——每個字詞都是孤立的單元格&#xff0c;計算機看到的只有冰冷的0和1&#xff0c;而“黛玉葬花”的凄美意境卻消失得無影無蹤。這就是NLP工…

2. kubernetes操作概覽

以下是 Kubernetes 的核心操作概覽&#xff0c;涵蓋常用命令、資源管理和典型場景的操作流程&#xff1a; 1. 核心操作工具 (1) kubectl 命令行工具 Kubernetes 的所有操作均通過 kubectl 實現&#xff0c;常用命令如下&#xff1a; 操作類型命令示例作用說明查看資源狀態ku…

從Ampere到Hopper:GPU架構演進對AI模型訓練的顛覆性影響

一、GPU架構演進的底層邏輯 AI大模型訓練效率的提升始終與GPU架構的迭代深度綁定。從Ampere到Hopper的演進路徑中&#xff0c;英偉達通過?張量核心升級?、?顯存架構優化?、?計算范式革新?三大技術路線&#xff0c;將LLM&#xff08;大語言模型&#xff09;訓練效率提升至…

p2p的發展

PCDN&#xff08;P2P內容分發網絡&#xff09;行業目前處于快速發展階段&#xff0c;面臨機遇與挑戰并存的局面。 一、發展機遇 技術融合推動 邊緣計算與5G普及&#xff1a;5G的高帶寬、低延遲特性與邊緣計算技術結合&#xff0c;顯著提升PCDN性能&#xff0c;降低延遲&#x…

計算機視覺與深度學習 | 視覺里程計(Visual Odometry, VO)學習思路總結

視覺里程計(Visual Odometry, VO)學習思路總結 視覺里程計(VO)是通過攝像頭捕獲的圖像序列估計相機運動軌跡的技術,廣泛應用于機器人、自動駕駛和增強現實等領域。以下是一個系統的學習路徑,涵蓋基礎理論、核心算法、工具及實踐建議:一、基礎理論與數學準備 核心數學工具…

Ubuntu 24.04 中文輸入法安裝

搜狗輸入法&#xff0c;在Ubuntu 24.04上使用失敗&#xff0c;安裝教程如下 https://shurufa.sogou.com/linux/guide 出現問題的情況&#xff0c;是這個帖子里描述的&#xff1a; https://forum.ubuntu.org.cn/viewtopic.php?t493893 后面通過google拼音輸入法解決了&#x…

阿里云 MSE Nacos 發布全新“安全防護”模塊,簡化安全配置,提升數據保護

作者&#xff1a;張文浩 阿里云在其微服務引擎&#xff08;MSE&#xff09;注冊配置中心 Nacos 上正式推出全新“安全防護”功能模塊&#xff0c;旨在幫助企業用戶有效管理安全狀態和降低開啟安全相關功能的學習成本&#xff0c;提升微服務架構的安全性。首期推出的“安全防護…

C#核心(23)StringBuilder

前言 我們先前已經了解了String的一些基本規則和常見的用法,今天就來講一下和string有所區別的StringBulider。 在 C# 中,StringBuilder 類是一個非常有用的工具,特別是在需要頻繁修改字符串時。與 String 類型不同,StringBuilder 類提供了一種動態字符串,可以在不創建新…

活動圖與流程圖的區別與聯系:深入理解兩種建模工具

目錄 前言1. 活動圖概述1.1 活動圖的定義1.2 活動圖的基本構成要素1.3 活動圖的應用場景 2. 流程圖概述2.1 流程圖的定義2.2 流程圖的基本構成要素2.3 流程圖的應用場景 3. 活動圖與流程圖的聯系4. 活動圖與流程圖的區別4.1 所屬體系不同4.2 表達能力差異4.3 使用目的與語境4.4…