Redis學習筆記 ----- 緩存

一、什么是緩存

緩存(Cache)是數據交換的緩沖區,是存儲數據的臨時地方,一般讀寫性能較高。

(一)緩存的作用

  • 降低后端負載:減少對數據庫等后端存儲的直接訪問壓力。
  • 提高讀寫效率,降低響應時間:利用緩存的高性能,快速響應數據請求。

(二)緩存的成本

  • 數據一致性成本:緩存與后端數據可能存在不一致,維護一致性需要額外處理。
  • 代碼維護成本:引入緩存后,代碼邏輯會更復雜,增加維護難度。
  • 運維成本:緩存系統的部署、監控、擴容等都需要投入運維資源。

二、緩存更新策略

策略說明一致性維護成本
內存淘汰利用Redis的內存淘汰機制,內存不足時自動淘汰部分數據,下次查詢時更新緩存
超時剔除給緩存數據添加TTL時間,到期后自動刪除緩存,下次查詢時更新緩存一般
主動更新編寫業務邏輯,在修改數據庫的同時更新緩存

業務場景

  • 低一致性需求:使用內存淘汰機制,例如店鋪類型的查詢緩存。
  • 高一致性需求:主動更新,并以超時剔除作為兜底方案,例如店鋪詳情查詢的緩存。

代碼實現:主動更新策略

@Override
@Transactional
public Result updateShop(Shop shop) {Long id = shop.getId();if (id == null) {return Result.fail("商鋪ID不能為空");}// 1.先更新數據庫updateById(shop);// 2.再刪除緩存(保證數據一致性)stringRedisTemplate.delete(CACHE_SHOP_KEY + shop.getId());return Result.ok();
}

三、緩存更新策略的最佳實踐方案

(一)低一致性需求

使用Redis自帶的內存淘汰機制。

(二)高一致性需求

主動更新,并以超時剔除作為兜底方案。

  • 讀操作
    • 緩存命中則直接返回。
    • 緩存未命中則查詢數據庫,并寫入緩存,設定超時時間。
  • 寫操作
    • 先寫數據庫,然后再刪除緩存。
    • 要確保數據庫與緩存操作的原子性。

代碼實現:基礎緩存讀寫邏輯

private Result queryShopById1(Long id) {// 1.查詢RedisString cacheShop = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);// 2.緩存命中直接返回if (StrUtil.isNotBlank(cacheShop)) {Shop shop = JSONUtil.toBean(cacheShop, Shop.class);return Result.ok(shop);}// 3.緩存未命中查詢數據庫Shop shop = getById(id);if (shop == null) {return Result.fail("商鋪不存在!");}// 4.數據庫查詢結果寫入Redis,設置超時時間stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);return Result.ok(shop);
}

四、緩存問題及解決方案

(一)緩存穿透

問題定義

客戶端請求的數據在緩存中和數據庫中都不存在,這樣緩存永遠不會生效,這些請求都會打到數據庫。

問題場景
  1. 惡意攻擊:黑客故意構造大量不存在的ID(如負數、超范圍的隨機數)發起請求,試圖耗盡數據庫資源
  2. 業務誤操作:前端表單未做校驗,用戶輸入了無效ID導致大量無效查詢
  3. 數據已刪除:商品已下架或用戶已注銷,但仍有請求訪問這些已不存在的數據
  4. 爬蟲抓取:爬蟲程序遍歷不存在的URL路徑,導致大量無效查詢
解決方案
1. 緩存空對象

原理:當數據庫查詢結果為空時,仍然將這個空結果緩存起來(可以是空字符串、null或特定標識),并設置較短的過期時間,避免同一無效請求反復穿透到數據庫。
在這里插入圖片描述

實現代碼

private Result queryShopById2(Long id) {// 1.查詢RedisString cacheShop = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);// 2.緩存命中(包括空值)if (StrUtil.isNotBlank(cacheShop)) {// 2.1 空值判斷if (cacheShop.isEmpty()) {return Result.fail("商鋪不存在!");}// 2.2 正常數據返回Shop shop = JSONUtil.toBean(cacheShop, Shop.class);return Result.ok(shop);}// 3.數據庫查詢Shop shop = getById(id);if (shop == null) {// 3.1 數據庫不存在則緩存空值(短期有效)stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, "", CACHE_NULL_TTL, TimeUnit.MINUTES);return Result.fail("商鋪不存在!");}// 4.正常數據寫入緩存stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);return Result.ok(shop);
}

優缺點

  • 優點:實現簡單,維護方便,能有效攔截重復的無效請求
  • 缺點:
    • 占用額外內存空間存儲空值
    • 可能造成短期數據不一致(如剛刪除的數據又被創建)
    • 對于海量不同的無效ID,仍可能消耗大量緩存空間
2. 布隆過濾器

原理:在緩存之前增加一層布隆過濾器,預先將數據庫中所有存在的Key存入布隆過濾器。當請求進來時,先通過布隆過濾器判斷Key是否可能存在:

  • 若不存在,則直接返回,無需訪問緩存和數據庫
  • 若可能存在,再走正常的緩存+數據庫查詢流程

實現思路

  1. 系統初始化時,將數據庫中所有有效ID加載到布隆過濾器
  2. 接收請求時,先通過布隆過濾器驗證ID有效性
  3. 對布隆過濾器判斷不存在的ID,直接返回錯誤

優缺點

  • 優點:內存占用少(相比緩存空對象),處理海量無效ID效率高
  • 缺點:
    • 實現復雜,需要維護布隆過濾器
    • 存在誤判可能(不能準確判斷元素是否存在)
    • 需要定期更新布隆過濾器數據,以反映數據庫變化

(二)緩存雪崩

問題定義

在同一時段大量的緩存key同時失效或者Redis服務宕機,導致大量請求到達數據庫,帶來巨大壓力,甚至引起數據庫宕機。

問題場景
  1. 集中過期:系統上線時為一批熱點數據設置了相同的過期時間(如24小時),導致24小時后這批數據同時失效
  2. Redis宕機:Redis服務器因硬件故障、網絡問題或內存溢出等原因突然宕機,整個緩存層失效
  3. 批量更新:電商平臺在大促前批量更新商品信息,導致大量緩存被刪除
  4. 緩存穿透引發:大量穿透請求導致數據庫壓力過大,進而影響緩存服務的正常運行
解決方案
1. 過期時間隨機化

原理:為不同的緩存Key設置基礎過期時間的同時,增加一個隨機偏移量(如±10%),避免大量Key在同一時間點同時過期。

實現代碼

// 設置過期時間時增加隨機值
int baseTtl = 30; // 基礎30分鐘
int random = new Random().nextInt(10); // 0-9分鐘隨機
stringRedisTemplate.opsForValue().set(key, value, baseTtl + random, TimeUnit.MINUTES
);
2. Redis集群

原理:通過部署Redis集群(主從+哨兵或Redis Cluster)提高緩存服務的可用性,避免單點故障導致整個緩存層失效。

關鍵措施

  • 主從復制:實現數據備份和讀寫分離
  • 哨兵機制:自動監控和故障轉移
  • 集群分片:分散數據存儲,提高整體容量和性能
3. 降級限流

原理:當緩存服務出現異常時,通過降級策略限制對數據庫的請求流量,保護數據庫不被壓垮。

實現方式

  • 使用熔斷器(如Sentinel、Hystrix)監控緩存服務狀態
  • 當緩存服務異常時,返回默認數據或提示信息,而非直接查詢數據庫
  • 對查詢數據庫的請求設置限流閾值,超出閾值的請求直接拒絕
4. 多級緩存

原理:構建多級緩存架構(如本地緩存+分布式緩存),即使分布式緩存失效,本地緩存仍能提供部分緩沖能力。

常見架構

  1. 本地緩存(Caffeine、Guava):存在于應用進程內,速度最快
  2. 分布式緩存(Redis):供多個應用實例共享
  3. 數據庫緩存:數據庫自身的緩存機制

(三)緩存擊穿

問題定義

也叫熱點Key問題,指一個被高并發訪問的熱點數據的緩存突然失效,無數請求會在瞬間同時到達數據庫,給數據庫帶來巨大沖擊。

問題場景
  1. 熱門商品:電商平臺的爆款商品緩存過期,恰逢促銷活動期間,大量用戶同時訪問
  2. 熱點事件:突發新聞事件的相關數據緩存過期,引發大量用戶同時查詢
  3. 排行榜:熱門游戲排行榜數據緩存過期,大量玩家同時刷新頁面
  4. 秒殺活動:秒殺商品的緩存過期,大量用戶在同一時間點搶購
解決方案
1. 互斥鎖

原理:當緩存失效時,不是讓所有請求都去查詢數據庫,而是通過鎖機制保證只有一個請求能去查詢數據庫并重建緩存,其他請求則等待緩存重建完成后再從緩存獲取數據。
在這里插入圖片描述

實現代碼

private Result queryShopById3(Long id) {// 1.查詢RedisString cacheShop = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);if (StrUtil.isNotBlank(cacheShop)) {if (cacheShop.isEmpty()) {return Result.fail("商鋪不存在!");}return Result.ok(JSONUtil.toBean(cacheShop, Shop.class));}Shop shop = null;try {// 2.獲取互斥鎖boolean isLock = tryLock(LOCK_SHOP_KEY + id);if (!isLock) {// 2.1 獲取鎖失敗則重試(短暫等待后再次查詢)Thread.sleep(50);return queryShopById3(id);}// 3.獲取鎖成功,查詢數據庫shop = getById(id);// 模擬緩存重建耗時操作Thread.sleep(200);if (shop == null) {stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, "", CACHE_NULL_TTL, TimeUnit.MINUTES);return Result.fail("商鋪不存在!");}// 4.寫入緩存stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, JSONUtil.toJsonStr(shop), CACHE_SHOP_TTL, TimeUnit.MINUTES);} catch (InterruptedException e) {throw new RuntimeException(e);} finally {// 5.釋放鎖unlock(LOCK_SHOP_KEY + id);}return Result.ok(shop);
}// 獲取鎖(使用Redis的setIfAbsent實現)
private boolean tryLock(String key) {return Boolean.TRUE.equals(stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 100, TimeUnit.SECONDS));
}// 釋放鎖
private void unlock(String key) {stringRedisTemplate.delete(key);
}

優缺點

  • 優點:
    • 保證數據一致性(每次都是最新數據)
    • 無額外內存消耗
    • 實現相對簡單
  • 缺點:
    • 鎖競爭會導致請求等待,影響接口響應性能
    • 可能存在死鎖風險(需設置合理的鎖過期時間)
    • 高并發下,第一個獲取鎖的請求可能成為性能瓶頸
2. 邏輯過期

原理:給緩存數據設置一個邏輯過期時間(而非Redis的實際過期時間),緩存永不過期。當查詢時發現數據已過邏輯過期時間,不直接刪除緩存,而是返回舊數據并異步更新緩存,保證后續請求能盡快獲取到新數據。
在這里插入圖片描述

實現代碼

// 線程池(用于異步重建緩存)
private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);// 邏輯過期數據封裝類
@Data
public class RedisData {// 邏輯過期時間private LocalDateTime expireTime;// 實際存儲的數據(如Shop對象)private Object data;
}private Result queryShopById4(Long id) {// 1.查詢RedisString cacheShop = stringRedisTemplate.opsForValue().get(CACHE_SHOP_KEY + id);if (StrUtil.isBlank(cacheShop)) {return Result.fail("商鋪不存在!");}if (cacheShop.isEmpty()) {return Result.fail("商鋪不存在!");}// 2.解析緩存數據(帶邏輯過期時間)RedisData redisData = JSONUtil.toBean(cacheShop, RedisData.class);JSONObject data = (JSONObject) redisData.getData();Shop shop = JSONUtil.toBean(data, Shop.class);LocalDateTime expireTime = redisData.getExpireTime();// 3.判斷是否邏輯過期if (expireTime.isAfter(LocalDateTime.now())) {// 3.1 未過期直接返回return Result.ok(shop);}// 3.2 已過期,嘗試獲取鎖重建緩存boolean isLock = tryLock(LOCK_SHOP_KEY + id);if (isLock) {// 3.2.1 獲取鎖成功,異步重建緩存(不阻塞當前請求)CACHE_REBUILD_EXECUTOR.submit(() -> {try {saveShop2Redis(id, CACHE_SHOP_TTL);} catch (InterruptedException e) {throw new RuntimeException(e);} finally {unlock(LOCK_SHOP_KEY + id);}});}// 3.2.2 無論是否獲取鎖,都返回舊數據(保證用戶體驗)return Result.ok(shop);
}// 封裝邏輯過期數據并寫入Redis
public void saveShop2Redis(long id, Long seconds) throws InterruptedException {Shop shop = getById(id);// 模擬緩存重建延時Thread.sleep(200);RedisData redisData = new RedisData();redisData.setData(shop);redisData.setExpireTime(LocalDateTime.now().plusSeconds(seconds));stringRedisTemplate.opsForValue().set(CACHE_SHOP_KEY + id, JSONUtil.toJsonStr(redisData));
}

優缺點

  • 優點:
    • 無請求阻塞,性能好(始終返回數據,不等待)
    • 避免大量請求同時沖擊數據庫
  • 缺點:
    • 數據可能短期不一致(返回舊數據)
    • 需要額外存儲過期時間,占用更多內存
    • 實現相對復雜,需要處理異步更新邏輯

五、緩存常量定義

public class RedisConstants {// 商鋪緩存鍵前綴public static final String CACHE_SHOP_KEY = "cache:shop:";// 商鋪緩存默認過期時間(30分鐘)public static final Long CACHE_SHOP_TTL = 30L;// 空值緩存過期時間(2分鐘,用于解決緩存穿透)public static final Long CACHE_NULL_TTL = 2L;// 商鋪緩存鎖鍵前綴(用于解決緩存擊穿)public static final String LOCK_SHOP_KEY = "lock:shop:";// 鎖過期時間(10秒)public static final Long LOCK_SHOP_TTL = 10L;
}

六、緩存方案對比

方案解決問題優點缺點適用場景
基礎緩存實現簡單存在緩存穿透、擊穿問題低并發、非核心業務
緩存空值緩存穿透防止數據庫壓力過大占用額外緩存空間無效請求相對集中的場景
布隆過濾緩存穿透內存占用少有誤判、實現復雜海量無效ID場景
互斥鎖緩存擊穿數據一致性好可能阻塞請求一致性要求高的熱點數據
邏輯過期緩存擊穿無阻塞,性能好可能返回舊數據高并發、一致性要求不高
隨機TTL緩存雪崩實現簡單只能解決集中過期問題所有需要設置過期的場景
多級緩存緩存雪崩提高可用性和性能實現復雜、維護成本高核心業務、高可用要求

參考來源

本文內容基于黑馬程序員《Redis入門到實戰教程》相關章節學習整理,部分代碼示例與知識點解析參考了該課程的講解。

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

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

相關文章

React響應式鏈路

文章目錄響應式鏈路的核心環節1.狀態定義與初始化2.狀態更新觸發(狀態變更)3.調度更新(Scheduler)4.重新渲染(Render 階段)5.協調(Reconciliation)與 Fiber 架構6.提交更新&#xff…

軟件設計師——計算機網絡學習筆記

一、計算機網絡 網絡基礎 1. 計算機網絡的分類2. 網絡拓撲結構 總線型(利用率低、干擾大、價格低) 星型(交換機形成的局域網、中央單元負荷大) 環型(流動方向固定、效率低擴充難) 樹型(總線型的擴充、分級結構) 分布式(任意節點連接、管理難成本高)一般來說,辦公室局…

1200 SCL學習筆記

一. IF. 如果。下面是一個起保停IF #I_start AND NOT #I_stop THEN //如果I_start接通 和 I_stop沒有接通#Q_run : 1; //輸出Q_run 接通 ELSIF #I_stop THEN //如果I_stop接通#Q_run : 0; //。。。。。。 END_IF;二. CASECASE…

單例模式與線程池

1. 單例模式單例模式是一種常用的設計模式,它確保一個類只有一個實例,并提供一個全局訪問點來獲取這個實例。這種模式在需要控制資源訪問、管理共享狀態或協調系統行為時非常有用。單例模式的核心特點:私有構造函數:防止外部通過n…

Chrome和Edge如何開啟暗黑模式

Edge和Chrome瀏覽器都提供了實驗性功能,可以通過修改實驗性設置來開啟暗黑模式。 在瀏覽器地址欄中輸入edge://flags/(Edge)或chrome://flags/(Chrome)。在搜索框中輸入“dark”,找到與暗黑模式相關的選項。…

【科研繪圖系列】浮游植物的溶解性有機碳與初級生產力的關系

禁止商業或二改轉載,僅供自學使用,侵權必究,如需截取部分內容請后臺聯系作者! 文章目錄 介紹 數據準備 數據處理 溶解性有機碳(DOC)與初級生產力(NPP)的關系 溶解性有機碳(DOC)與光照強度(PAR)的關系 數據可視化 加載R包 數據下載 導入數據 畫圖1 畫圖2 總結 系統信…

IDEA相關的設置和技巧

IDEA相關的設置和技巧 我的博客對應文章地址 1.布局設置 IDEA的布局自定義程度很高,頂部工具欄,側邊欄都可以隨意定制,設置好的布局方案可以保存,在新項目中快速使用 1.1 工具欄設置 [!tip] 舉個例子:比如我要在頂部…

AWS Lambda 完全指南:解鎖無服務器架構的強大力量

在云計算的發展浪潮中,無服務器(Serverless) 架構已然成為構建現代應用的新范式。而在這場變革的中心,AWS Lambda 作為開創性的 Function-as-a-Service (FaaS) 服務,徹底改變了我們部署和運行代碼的方式。 本文將帶您深入探索 AWS Lambda,從核心概念、工作原理到高級實踐…

人工智能時代下普遍基本收入(UBI)試驗的實踐與探索——以美國硅谷試點為例

一、硅谷UBI試驗的最新進展(2025年)1. 試驗規模與資金來源圣克拉拉縣試點:硅谷所在地圣克拉拉縣針對脫離寄養家庭的年輕人開展UBI試驗,每月發放1000美元補貼,持續1-2年,覆蓋約60名參與者,成本約…

云計算之云主機Linux是什么?有何配置?如何選?

一、云環境如何選擇Linux發行版 1.1、Linux在各個領域的發展 Linux在各個領域的發展序號Linux發展領域說明1Linux在服務器領域的發展目前Linux在服務器領域已經占據95%的市場份額,同時Linux在服務器市場的迅速崛起,已經引起全球IT產業的高度關注&#xf…

XCVU13P-2FHGB2104E Xilinx(AMD)Virtex UltraScale+ FPGA

XCVU13P-2FHGB2104E 是 Xilinx(AMD)Virtex UltraScale FPGA 系列中的一款高性能芯片,適用于需要大量邏輯資源、高帶寬和高速數據傳輸的應用場景。作為該系列中的旗艦產品,XCVU13P-2FHGB2104I 結合了強大的處理能力和靈活的可編程性…

自動化單詞例句獲取系統設計方案

方案一 (網絡爬蟲) 這個方案的核心思路是:創建一個自動化的腳本,該腳本會讀取你 MongoDB 中的單詞,然后去一個免費的在線詞典網站上抓取這些單詞的例句,最后將抓取到的例句存回你的 MongoDB 數據庫中對應的單詞條目下。 一、 核心思路與技術選型 自動化腳本: 我們將使用 P…

WPF Alert彈框控件 - 完全使用指南

WPF Alert彈框控件 - 完全使用指南概述快速開始nuget安裝與引用基本用法功能特性詳細說明AlertType 枚舉方法參數詳解Show 方法(局部彈窗)ShowGlobal 方法(全局彈窗)完整示例代碼XAML 布局C# 代碼實現界面演示功能特性對比表格自定…

可視化-模塊1-HTML-01

1-軟件下載: 軟件名稱:HBuilderX 官網地址: https://www.dcloud.io/hbuilderx.html 下載文佳-解壓縮-打開exe文件 創建快捷方式至桌面 2-創建項目 【普通項目】-【基本HTML項目】-【項目名:week1-1】 【index】輸入&#xff1…

機器翻譯 (Machine Translation) 經典面試筆試50題(包括詳細答案)

更多內容請見: 機器翻譯修煉-專欄介紹和目錄 文章目錄 第一部分:基礎理論與概念 (1-15題) 1. 題目: 什么是機器翻譯(MT)?請簡述其發展歷程中的幾個主要范式。 2. 題目: 機器翻譯的主要評價指標有哪些?請詳細解釋BLEU指標的計算原理和優缺點。 3. 題目: 什么是平行語料…

linux中文本文件操作之grep命令

文章目錄背景案例demo環境方式一、安裝wsl方式二、安裝grep一、查找指定字符串二、忽略大小寫查找三、查找時顯示行號四、統計匹配的次數五、精準匹配一個單詞六、顯示匹配上下文七、只顯示匹配的內容八、按固定字符串匹配背景 在日常運維中會對日志文件,使用grep命…

鏈表漫游指南:C++ 指針操作的藝術與實踐

文章目錄0. 前言1. 鏈表的分類2. 單鏈表的實現2.1 鏈表的基本結構——節點(Node)2.2 核心操作詳解2.2.1 構造和析構2.2.2 插入操作2.2.3 刪除操作2.3.4 其他操作2.4 總結3. 雙向鏈表的實現3.1 基本結構設計3.2 基本操作3.2.1 初始化與銷毀3.2.2 插入與刪…

Claude Code賦能企業級開發:外賣平臺核心系統的智能化重構

開篇:萬億市場背后的技術挑戰中國外賣市場日訂單量超過1億單,每一單背后都是一個復雜的技術鏈條:用戶下單→商家接單→騎手搶單→實時配送→評價反饋。構建這樣一個支撐千萬級并發、涉及地理位置計算、實時調度、支付結算的超級平臺&#xff…

【使用Unsloth 微調】數據集的種類

1. 什么是數據集 對于大型語言模型(LLMs),數據集是用于訓練模型的數據集合。為了訓練有效,文本數據需要能夠被分詞(tokenized)。創建數據集的關鍵部分之一是聊天模板(chat template)…

【碼蹄杯】2025年本科組省賽第一場

個人主頁:Guiat 歸屬專欄:算法競賽 文章目錄1. MC0455 四大名著-西游簽到2. MC0456 斬斷靈藤3. MC0457 符咒封印4. MC0458 移鐵術5. MC0459 昆侖墟6. MC0460 星空迷軌陣7. MC0461 排隊8. MC0462 最后一難正文 總共8道題。 1. MC0455 四大名著-西…