什么是緩存穿透、緩存雪崩、緩存擊穿?

?什么是緩存?

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

怎么防止緩存穿透?

緩存穿透是指客戶端請求的數據在緩存中和數據庫中都不存在,這樣緩存永遠不會生效,這些請求都會打到數據庫,給數據庫帶來巨大壓力。

常見的解決方案有兩種:

緩存空對象

客戶端第一次請求是,發現數據庫中數據不存在,在緩存中設置該值為空串,后面的請求中若發現緩存中存儲的值為空串直接返回空串,不在查詢數據庫。(緩存需要設置一定時間內過期,防止數據庫中有數據后緩存仍然為空。或者在插入數據時,清空緩存)

  • ????????優點:實現簡單,維護方便
  • ????????缺點:額外的內存消耗,可能造成短期的不一致

    /*** 設置空值解決緩存穿透*/public Shop queryWithPassThrough(Long id){//先從redis查詢商鋪緩存,若存在,從redis中返回,否則查詢數據庫,存在寫入redis,并返回String shopJson = stringRedisTemplate.opsForValue().get("cache:shop:" + id);if(StringUtils.isNotBlank(shopJson)){Shop shop = JSONUtil.toBean(shopJson, Shop.class);return shop;}//判斷命中的是否為空,防止緩存穿透if(shopJson==null){ //若為null,說明redis中數據為空字符串,說明mysql數據庫也沒有數據return null;}//redis不存在,查詢數據庫Shop shop = getById(id);if(shop==null){//將空值寫入redisstringRedisTemplate.opsForValue().set("cache:shop:"+id,"",30, TimeUnit.MINUTES);return null;}stringRedisTemplate.opsForValue().set("cache:shop:"+id,JSONUtil.toJsonStr(shop));return shop;}

布隆過濾

  • ?????優點:內存占用較少,沒有多余key
  • ?????缺點:實現復雜、存在誤判可能

其他方案:

?????????增強id的復雜度,避免被猜測id規律

?????????做好數據的基礎格式校驗

?????????加強用戶權限校驗

?????????做好熱點參數的限流

為什么會出現緩存雪崩?

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

解決方案:

  • 給不同的Key的TTL添加隨機值
  • 利用Redis集群提高服務的可用性
  • 給緩存業務添加降級限流策略
  • 給業務添加多級緩存

如何解決緩存擊穿?

緩存擊穿問題也叫熱點Key問題,就是一個被高并發訪問并且緩存重建業務較復雜的key突然失效了,無數的請求訪問會在瞬間給數據庫帶來巨大的沖擊。

常見的解決方案有兩種:

互斥鎖

?在高并發環境下,當有一個線程獲得鎖訪問數據庫時,其他線程等待。假如業務A需要獲取緩存A和緩存B,而業務B需要獲取緩存B和緩存A。此時業務A已經獲取了緩存A的鎖正在等待緩存B,而業務B獲取了緩存B的鎖等待緩存A,就出現了互相等待的情況,產生死鎖。

? ? ? ? 優點:沒有額外的內存消耗,保證一致性,實現簡單

? ? ? ? 缺點:線程需要等待,性能受影響,可能有死鎖風險

   /*** 在設置空值,已經解決緩存穿透的基礎上,添加互斥鎖解決緩存擊穿*/public Shop queryWithMutex (Long id){//先從redis查詢商鋪緩存,若存在,從redis中返回,否則查詢數據庫,存在寫入redis,并返回String shopJson = stringRedisTemplate.opsForValue().get("cache:shop:" + id);if(StringUtils.isNotBlank(shopJson)){Shop shop = JSONUtil.toBean(shopJson, Shop.class);return shop;}//判斷命中的是否為空,房子緩存穿透if(shopJson==null){ //部位null,說明redis中數據為空字符串,說明mysql數據庫也沒有數據return null;}//redis不存在,失效緩存重建//獲取互斥鎖,每個店鋪創建一個鎖String LockKey = "lock:shop:"+id;Shop shop = null;try {boolean isLock = tryLock(LockKey);//獲取鎖失敗,休眠重試if(!isLock){Thread.sleep(50);return queryWithMutex(id);}//獲取到鎖,查詢數據庫shop = getById(id);if(shop==null){//將空值寫入redisstringRedisTemplate.opsForValue().set("cache:shop:"+id,"",30, TimeUnit.MINUTES);return null;}stringRedisTemplate.opsForValue().set("cache:shop:"+id,JSONUtil.toJsonStr(shop));}catch (InterruptedException e){throw  new RuntimeException("系統異常");}finally {//釋放鎖unLock(LockKey);}return shop;}/***獲取鎖*/private boolean tryLock(String key){Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}/*** 釋放鎖*/private void unLock(String key){stringRedisTemplate.delete(key);}

邏輯過期:

? ? ? ? 優點:線程無需等待,性能較好

? ? ? ? 缺點:不保證一致性,有額外內存消耗,實現復雜

//線程池 
private static final ExecutorService CACHE_REBUILD_EXECUTOR = Executors.newFixedThreadPool(10);/*** 使用邏輯過期解決緩存擊穿(實際上數據不會過期,故不需要考慮緩存穿透問題),使用時需要先緩存熱點數據*/public Shop queryWithLogicalExpire (Long id){//先從redis查詢商鋪緩存,若存在,從redis中返回,否則查詢數據庫,存在寫入redis,并返回String shopJson = stringRedisTemplate.opsForValue().get("cache:shop:" + id);//緩存中不存在,直接返回null。(熱點數據,通過一般需要自行初始化到redis緩存中,一般不會出現null的情況)if(StringUtils.isBlank(shopJson)){return null;}RedisData redisData = JSONUtil.toBean(shopJson, RedisData.class);Shop shop = JSONUtil.toBean((JSONObject) redisData.getData(), Shop.class);//獲取緩存數據LocalDateTime expireTime = redisData.getExpireTime();//獲取緩存過期時間//判斷是否過期if(expireTime.isAfter(LocalDateTime.now())){//未過期,直接返回return shop;}//過期,需要緩存重建//獲取互斥鎖String LockKey = "lock:shop:"+id;if(tryLock(LockKey)){//獲取鎖成功,開啟獨立線程,實現緩存重建CACHE_REBUILD_EXECUTOR.submit(() ->{//重建緩存try{this.saveShop2Redis(id,20L);}catch (Exception e){throw new RuntimeException(e);}finally {//釋放鎖unLock(LockKey);}});}return shop;}public void saveShop2Redis(Long id,Long expireSeconds){//查詢店鋪數據Shop shop = getById(id);//封裝邏輯過期時間RedisData redisData = new RedisData();redisData.setData(shop);redisData.setExpireTime(LocalDateTime.now().plusMinutes(expireSeconds));//寫入redisstringRedisTemplate.opsForValue().set("cache:shop:"+id,JSONUtil.toJsonStr(redisData));}/***獲取鎖*/private boolean tryLock(String key){Boolean flag = stringRedisTemplate.opsForValue().setIfAbsent(key, "1", 10, TimeUnit.SECONDS);return BooleanUtil.isTrue(flag);}/*** 釋放鎖*/private void unLock(String key){stringRedisTemplate.delete(key);}

全局ID生成器

如果使用數據庫自增ID就存在一些問題:

  • id的規律性太明顯
  • 受單表數據量的限制

全局ID生成器,是一種在分布式系統下用來生成全局唯一ID的工具,一般要滿足下列特性:

為了增加ID的安全性,我們可以不直接使用Redis自增的數值,而是拼接一些其它信息:

ID的組成部分:

  • 符號位:1bit,永遠為0
  • 時間戳:31bit,以秒為單位,可以使用69
  • 序列號:32bit,秒內的計數器,支持每秒產生2^32個不同ID

Redis自增ID策略:

  • 每天一個key,方便統計訂單量
  • ID構造是 時間戳 + 計數器
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;@Component
public class RedisIdWorker {private StringRedisTemplate stringRedisTemplate;public RedisIdWorker(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}// 2022年開始時間戳private static final long BEGIN_TIMESTAMP = 1640995200L;// 序列號位數private static final int COUNT_BITS = 32;public long nextId(String KeyPrefix) {// 1.生成時間戳LocalDateTime now = LocalDateTime.now();long nowSecond = now.toEpochSecond(ZoneOffset.UTC);long timestamp = nowSecond-BEGIN_TIMESTAMP;// 2.生成序列號String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));//自增長,返回自增序列號,key不存在會自動創建一個keylong count = stringRedisTemplate.opsForValue().increment("icr:" + KeyPrefix + ":" + date);// 3.拼接并返回// 時間戳左移32位,通過|運算,拼接序列號return timestamp << COUNT_BITS | count;}
}

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

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

相關文章

深度學習在自動駕駛車輛車道檢測中的應用

引言 自動駕駛技術是人工智能領域的一個前沿方向&#xff0c;而車道檢測是實現自動駕駛的關鍵技術之一。通過識別和跟蹤車道線&#xff0c;自動駕駛車輛能夠保持在車道內行駛&#xff0c;提高行車安全。本文將詳細介紹如何使用深度學習技術進行車道檢測&#xff0c;并提供一個…

大模型如何引爆餐飲與電商行業變革

大模型如何引爆餐飲與電商行業變革&#xff1f; 一、時代背景&#xff1a;大模型重構產業邏輯的底層動力 1. 技術躍遷催生效率革命 2025年&#xff0c;大模型技術迎來"普惠臨界點"。李開復在中關村論壇指出&#xff0c;大模型推理成本每年降低10倍&#xff0c;使得…

chromium魔改——繞過無限debugger反調試

在進行以下操作之前&#xff0c;請確保已完成之前文章中提到的 源碼拉取及編譯 部分。 如果已順利完成相關配置&#xff0c;即可繼續執行后續操作。 在瀏覽器中實現“無限 debugger”的反調試技術是一種常見的手段&#xff0c;用于防止他人通過開發者工具對網頁進行調試或逆向…

在win11 環境下 新安裝 WSL ubuntu + 換國內鏡像源 + ssh + 桌面環境 + Pyhton 環境 + vim 設置插件安裝

在win11 環境下 新安裝 WSL ubuntu ssh gnome 桌面環境 Pyhton 環境 vim 設置插件安裝 簡單介紹詳細流程換國內鏡像源安裝 ssh 桌面環境python 環境vim 設置插件安裝 簡單介紹 內容有點長&#xff0c;這里就先簡單描述內容了。主要是快速在 Win11 搭建一個 wsl 的 linux 環…

python 命名空間與作用域 可變與不可變對象 閉包

python 命名空間與作用域 可變與不可變對象 閉包 作用域規則順序為&#xff1a; L->E->G->B 如果變量在局部內找不到&#xff0c;便會去局部外的局部找&#xff08;例如閉包&#xff09;&#xff0c;再找不到就會去全局找&#xff0c;再找不到就去內置中找。 若要在函…

安裝 TabbyAPI+Exllamav2 和 vLLM 的詳細步驟

在 5090 顯卡上成功安裝 TabbyAPIExllamav2 和 vLLM 并非易事&#xff0c;經過一番摸索&#xff0c;我總結了以下詳細步驟&#xff0c;希望能幫助大家少走彎路。 重要提示&#xff1a; 用戶提供的 PyTorch 安裝使用了 cu128&#xff0c;這并非標準 CUDA 版本。請根據你的系統實…

使用url-loader處理圖片等資源文件

&#x1f90d; 前端開發工程師、技術日更博主、已過CET6 &#x1f368; 阿珊和她的貓_CSDN博客專家、23年度博客之星前端領域TOP1 &#x1f560; 牛客高級專題作者、打造專欄《前端面試必備》 、《2024面試高頻手撕題》、《前端求職突破計劃》 &#x1f35a; 藍橋云課簽約作者、…

EIP-712:類型化結構化數據的哈希與簽名

1. 引言 以太坊 EIP-712: 類型化結構化數據的哈希與簽名&#xff0c;是一種用于對類型化結構化數據&#xff08;而不僅僅是字節串&#xff09;進行哈希和簽名 的標準。 其包括&#xff1a; 編碼函數正確性的理論框架&#xff0c;類似于 Solidity 結構體并兼容的結構化數據規…

contourformer:實時的輪廓分割transformer

論文地址:https://arxiv.org/abs/2501.17688 github:https://github.com/talebolano/Contourformer 模型結構 框架建立在 D-FINE 對象檢測模型之上,并將邊界框的回歸擴展到輪廓的回歸。為了實現高效的訓練,Contourformer 采用迭代方法進行輪廓變形,并引入降噪機制來加速…

【JavaScript】原型鏈 prototype 和 this 關鍵字的練習(老虎機)

這個老虎機練習主要考察JavaScript中的原型鏈&#xff08;prototype&#xff09;和this關鍵字的使用。 主要思路 創建三個輪盤&#xff08;reels&#xff09;實例&#xff1a;我們需要創建3個獨立的輪盤對象&#xff0c;它們都委托&#xff08;delegate&#xff09;到基礎的ree…

vue項目data functions should return an object

在vue項目中提示錯誤&#xff0c;data functions should return an object Message.error(err)錯了&#xff0c;Message.error()是element-ui的組件&#xff0c;只能接受字符串&#xff0c;不能接受對象。 改為Message.error(err.message)就好了 我的錯誤是 Message.error(er…

leetcode刷題 - 數組理論基礎

數組是內存空間連續存儲、相同類型數據的集合。遍歷方式&#xff1a;下標索引 下標&#xff1a;從 0 開始 數組的元素不能刪除&#xff0c;只能覆蓋 定義一維數組&#xff1a; int arr0[10]; int arr1[10] { 100, 90,80,70,60,50,40,30,20,10 }; int arr2[ ] { 100,90,80,7…

狀態機思想編程練習

狀態機實現LED流水燈 本次實驗&#xff0c;我們將利用狀態機的思想來進行Verilog編程實現一個LED流水燈&#xff0c;并通過Modelsim來進行模擬仿真&#xff0c;再到DE2-115開發板上進行驗證。 ? 首先進行主要代碼的編寫。 module led (input sys_clk,input sys_…

數據結構|排序算法(一)快速排序

一、排序概念 排序是數據結構中的一個重要概念&#xff0c;它是指將一組數據元素按照特定的順序進行排列的過程&#xff0c;默認是從小到大排序。 常見的八大排序算法&#xff1a; 插入排序、希爾排序、冒泡排序、快速排序、選擇排序、堆排序、歸并排序、基數排序 二、快速…

如何確保MQ消息隊列不丟失:Java實現與流程分析

前言 在分布式系統中&#xff0c;消息隊列&#xff08;Message Queue, MQ&#xff09;是核心組件之一&#xff0c;用于解耦系統、異步處理和削峰填谷。然而&#xff0c;消息的可靠性傳遞是使用MQ時需要重點考慮的問題。如果消息在傳輸過程中丟失&#xff0c;可能會導致數據不一…

關于termux運行pc交叉編譯的aarch64 elf的問題

在Linux系統上交叉編譯Nim程序到Android Termux環境需要特殊處理&#xff0c;以下是詳細的解決方案&#xff1a; 問題根源分析 ??ABI不兼容?? Android使用bionic libc而非標準glibc&#xff0c;直接編譯的Linux ARM二進制無法直接運行 ??動態鏈接錯誤?? 默認編譯會鏈…

為PXIe控制器配置NI Linux實時操作系統安裝軟件

一、升級BIOS 使用NI Linux Real-Time操作系統的PXI硬件支持頁面來確定NI Linux Real-Time是否支持您的PXIe控制器&#xff0c;以及是否需要更新控制器BIOS。 按照BIOS下載頁面上的“安裝說明”部分安裝BIOS更新。 注意&#xff1a;NI在NI 2020軟件版本中刪除對cRIO的Phar Lap和…

《汽車噪聲控制》課程作業

作業內容 在MATLAB繪制給出單個正弦波或余弦波的時域圖和頻域圖 繪制實測數據的時域圖和頻域圖 圖1 單個正弦波的時頻圖 圖1 單個正弦波的時頻圖 % 正弦波參數設置 f0 1000; % 信號頻率 1kHz Fs 16384; % 采樣頻率 16kHz T 0.05; % 信號持續時間 0.05秒 A 0.8; % 信號幅度…

Baklib內容中臺AI技術協同應用

內容中臺與AI協同創新 在數字化轉型進程中&#xff0c;內容中臺通過人工智能技術的深度整合&#xff0c;正重塑企業信息管理范式。以Baklib內容中臺為例&#xff0c;其通過智能語義分析引擎解析用戶意圖&#xff0c;結合知識圖譜構建技術動態關聯碎片化信息&#xff0c;實現從…

壓測工具開發實戰篇(二)——構建側邊欄以及設置圖標字體

你好&#xff0c;我是安然無虞。 文章目錄 構建側邊欄QtAwesome使用調整側邊欄寬度了解: sizePolicy屬性偽狀態 在閱讀本文之前, 有需要的老鐵可以先回顧一下上篇文章: 壓測工具開發(一)——使用Qt Designer構建簡單界面 構建側邊欄 我們要實現類似于下面這樣的側邊欄功能: …