Redis-緩存穿透擊穿雪崩

1. 穿透問題

緩存穿透問題就是查詢不存在的數據。在緩存穿透中,先查緩存,緩存沒有數據,就會請求到數據庫上,導致數據庫壓力劇增。

解決方法:

  1. 給不存在的key加上空值,防止每次都會請求到數據庫。
  2. 布隆過濾器,做一次過濾

1.1 使用緩存空值解決緩存擊穿問題

  1. 根據id=1來請求
  2. redis存在數據
    2.1. 存儲的是空值{},那么返回null
    2.2. 存儲的不是空值,說明存儲的是真實的數據庫數據
  3. redis不存在數據
  4. 查詢數據庫
    4.1. 數據庫存在數據,那么緩存數據到redis,返回真實的數據
    4.2. 數據庫不存在數據,那么緩存空對象 {},設置一個過期時間,返回空

@Component
public class RedisCacheClient {private final StringRedisTemplate stringRedisTemplate;public RedisCacheClient(StringRedisTemplate stringRedisTemplate) {this.stringRedisTemplate = stringRedisTemplate;}private void set(String key, Object value, Long time, TimeUnit timeUnit) {stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), time, timeUnit);}private String get(String key) {return stringRedisTemplate.opsForValue().get(key);}public <ID, R> R queryWithPassThrough(String keyPrefix, ID id, Class<R> clazz,Function<ID, R> dbFallBack, Long time, TimeUnit unit) {String key = keyPrefix + id;// 1.從redis查詢數據String json = get(key);// 2.判斷數據是否存在if (RedisConstants.EMPTY_OBJECT_JSON.equals(json)) {return null; //緩存的空對象值}if (StrUtil.isNotEmpty(json)) {return JSONUtil.toBean(json, clazz);}// 3.不存在,根據id查詢數據庫R r = dbFallBack.apply(id);if (r != null) {set(key, r, time, unit);return r;}// 4.存儲空對象set(key, RedisConstants.EMPTY_OBJECT_JSON /*{}*/, RedisConstants.CACHE_NULL_TTL, TimeUnit.SECONDS);return null;}
}

1.2 使用布隆過濾器做初次判斷

對于惡意攻擊,向服務器請求大量不存在的數據造成的緩存穿透,還可以用布隆過濾器先做一次過濾,對于不存在的數據,布隆過濾器一般都能夠過濾掉,不讓請求再往后端發送。當布隆過濾器說某個值存在時,這個值可能不存在;當它說不存在時,那就肯定不存在。

在這里插入圖片描述

布隆過濾器就是一個大型的位數組和幾個不一樣的無偏 hash 函數。所謂無偏就是能夠把元素的 hash 值算得比較均勻。

向布隆過濾器中添加 key 時,會使用多個 hash 函數對 key 進行 hash 算得一個整數索引值然后對位數組長度 進行取模運算得到一個位置,每個 hash 函數都會算得一個不同的位置。再把位數組的這幾個位置都置為 1 就 完成了 add 操作。向布隆過濾器詢問 key 是否存在時,跟 add 一樣,也會把 hash 的幾個位置都算出來,看看位數組中這幾個位置是否都為 1,只要有一個位為 0,那么說明布隆過濾器中這個key 不存在。如果都是 1,這并不能說明這個key 就一定存在,只是極有可能存在,因為這些位被置為 1 可能是因為其它的 key 存在所致。如果這個位數組比較稀疏,這個概率就會很大,如果這個位數組比較擁擠,這個概率就會降低。

這種方法適用于數據命中不高、 數據相對固定、 實時性低(通常是數據集較大) 的應用場景, 代碼維護較為復雜, 但是緩存空間占用很少。


1.2.1 導入pom坐標
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.13.6</version>
</dependency>
1.2.2 布隆過濾器代碼示例
class Main {private RedissonClient redissonClient;void test() {RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter("orderList");// 1.初始化布隆過濾器:預計元素為100000000L,誤判率為3%,根據這兩個參數會計算出底層的bit數組大小bloomFilter.tryInit(100000000L, 0.03);// 2.添加元素到bloomFilterbloomFilter.add("ayuan");// 3.判斷下面的數據是否在布隆過濾器中System.out.println(bloomFilter.contains("asheng"));System.out.println(bloomFilter.contains("longge"));System.out.println(bloomFilter.contains("ayuan"));}
}

使用布隆過濾器需要把所有數據提前放入布隆過濾器,并且在增加數據時也要往布隆過濾器里放,布隆過濾器緩存過濾偽代碼:
在這里插入圖片描述

1.2.3 布隆過濾器實戰
class Main {@Autowiredprivate RedissonClient redissonClient;private RBloomFilter<String> bloomFilter;@PostConstructvoid init() {// 1.初始化布隆過濾器bloomFilter = redissonClient.getBloomFilter("orderList");// 初始化布隆過濾器:預計元素為100000000L,誤判率為3%,根據這兩個參數會計算出底層的bit數組大小bloomFilter.tryInit(100000000L, 0.03);// 2.加載所有的數據加載到布隆過濾器// for (String key : keys) {//     bloomFilter.add(key);// }}@TestString get(String key) {// 3.從布隆過濾器這一級緩存判斷key是否存在boolean isContains = bloomFilter.contains(key);if (!isContains) {return "";}// 4.業務邏輯開發}
}

但是布隆過濾器無法刪除某一個元素,如果要刪除得重新初始化數據

2. 擊穿問題

緩存擊穿中,請求的 key 對應的是熱點數據 ,該數據存在于數據庫中,但不存在于緩存中(通常是因為緩存中的那份數據已經過期) 。這就可能會導致瞬時大量的請求直接打到了數據庫上,對數據庫造成了巨大的壓力,可能直接就被這么多請求弄宕機了。
[圖片]


解決方案:

  1. 基于互斥鎖(看情況):在緩存過期后,通過設置互斥鎖確保只有一個請求去查詢數據庫并且更新緩存。
  2. 提前預熱(推薦):針對熱點數據提前預熱,并將其入緩存中并設置合理的過期事件,比如:秒殺場景下的數據在秒殺結束前永不過期。
  3. 數據永不過期(不推薦):設置熱點數據永不過期或者過期時間比較長。

2.1 基于互斥鎖解決緩存擊穿問題

@Component
public class RedisCacheClient {private final StringRedisTemplate stringRedisTemplate;private final RedissonClient redissonClient;public RedisCacheClient(StringRedisTemplate stringRedisTemplate, RedissonClient redissonClient) {this.stringRedisTemplate = stringRedisTemplate;this.redissonClient = redissonClient;}private void set(String key, Object value, Long time, TimeUnit timeUnit) {stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(value), time, timeUnit);}private String get(String key) {return stringRedisTemplate.opsForValue().get(key);}public <ID, R> R query(String keyPrefix, ID id, Class<R> clazz,Function<ID, R> dbFallBack, Long time, TimeUnit unit) {String key = keyPrefix + id;// 1.從redis查詢數據String json = get(key);// 2.判斷數據是否存在if (RedisConstants.EMPTY_OBJECT_JSON.equals(json)) {return null; //緩存的空對象值}if (StrUtil.isNotEmpty(json)) {return JSONUtil.toBean(json, clazz);}//加鎖,防止緩存擊穿問題 -> redis的熱點key問題RLock redissonClientLock = redissonClient.getLock(RedisConstants.DISTRIBUTED_LOCK + key);redissonClientLock.lock(); //加鎖try {//dcl判斷鎖是否存在了json = get(key);if (json != null) {return queryWithPassThrough(keyPrefix, id, clazz, dbFallBack, time, unit);}//3. 不存在,根據id查詢數據庫R r = dbFallBack.apply(id);if (r != null) {set(key, r, time, unit);return r;}// 存儲空對象set(key, RedisConstants.EMPTY_OBJECT_JSON, RedisConstants.CACHE_NULL_TTL, TimeUnit.SECONDS);return null;} finally {redissonClientLock.unlock();}}
}

3. 雪崩問題

緩存宕機或者在同一時間大面積的失效,導致大量的請求都直接落到了數據庫上,對數據庫造成了巨大的壓力。

在這里插入圖片描述


解決方式:

  1. 設置隨機失效時間(可選):為緩存設置隨機的失效時間,例如在固定過期時間的基礎上加上一個隨機值,這樣可以避免大量緩存同時到期,從而減少緩存雪崩的風險。(例如:批量導入數據到redis的時候,如果設置過期時間一致,那么就會數據就會在同一時刻過期刪除)。
  2. 多級緩存:設計多級緩存,例如本地緩存+Redis 緩存的二級緩存組合,當 Redis 緩存出現問題時,還可以從本地緩存中獲取到部分數據。
  3. Redis集群:采用 Redis 集群,避免單機出現問題整個緩存服務都沒辦法使用。比如:Redis Sentinel哨兵集群、Redis Cluster分片集群。
  4. 限流:如果發現讀請求太多,可以采用限流的策略。

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

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

相關文章

如何在自己的網站接入API接口獲取數據?分步指南與實戰示例

將第三方API接入自己的網站是獲取實時數據、擴展功能的重要手段&#xff08;如展示商品、同步訂單、用戶登錄等&#xff09;。以下是完整的接入流程與關鍵實踐&#xff0c;以微店API為例&#xff0c;適用于多數開放平臺。 一、準備工作&#xff1a;注冊與權限申請 注冊開發者…

刷leetcode hot100--動態規劃3.12

第一題乘積max子數組[1h] emmmm感覺看不懂題解 線性dp【計劃學一下acwing&#xff0c;挨個做一下】 線性動態規劃 相似題解析 最長上升子序列 最大上升子序列和 最大連續子段和 乘積最大子數組_嗶哩嗶哩_bilibili 比較奇怪的就是有正負數和0&#xff0c;如何處理&#xff1f…

Pytortch深度學習網絡框架庫 torch.no_grad方法 核心原理與使用場景

在PyTorch中&#xff0c;with torch.no_grad() 是一個用于臨時禁用自動梯度計算的上下文管理器。它通過關閉計算圖的構建和梯度跟蹤&#xff0c;優化內存使用和計算效率&#xff0c;尤其適用于不需要反向傳播的場景。以下是其核心含義、作用及使用場景的詳細說明&#xff1a; 一…

postgresql 數據庫使用

目錄 索引 查看索引 創建 刪除索引 修改數據庫時區 索引 查看索引 select * from pg_indexes where tablenamet_table_data; 或者 select * from pg_statio_all_indexes where relnamet_table_data; 創建 CREATE INDEX ix_table_data_time ON t_table_data (id, crea…

為什么大模型網站使用 SSE 而不是 WebSocket?

在大模型網站&#xff08;如 ChatGPT、Claude、Gemini 等&#xff09;中&#xff0c;前端通常使用 EventSource&#xff08;Server-Sent Events, SSE&#xff09; 來與后端對接&#xff0c;而不是 WebSocket。這是因為 SSE 更適合類似流式文本生成的場景。下面我們詳細對比 SSE…

TDengine 數據對接 EXCEL

簡介 通過配置使用 ODBC 連接器&#xff0c;Excel 可以快速訪問 TDengine 的數據。用戶可以將標簽數據、原始時序數據或按時間聚合后的時序數據從 TDengine 導入到 Excel&#xff0c;用以制作報表整個過程不需要任何代碼編寫過程。 前置條件 準備以下環境&#xff1a; TDen…

【具身相關】legged_gym, isaacgym、rsl_rl關系梳理

【legged_gym】legged_gym, isaacgym代碼邏輯梳理 總體關系IsaacGymlegged_gymrsl_rl三者的關系 legged_gym代碼庫介紹環境模塊env 總體關系 IsaacGym Isaac Gym 是 NVIDIA 開發的一個高性能物理仿真平臺&#xff0c;專門用于強化學習和機器人控制任務。它基于 NVIDIA 的 Phy…

【每日學點HarmonyOS Next知識】狀態變量、動畫UI殘留、Tab控件顯示、ob前綴問題、文字背景拉伸

1、HarmonyOS 怎么用一個變量觀察其他很多個變量的變化&#xff1f; 有一個提交按鈕的顏色&#xff0c;需要很多個值非空才變為紅色&#xff0c;否則變為灰色&#xff0c;可不可以用一個變量統一觀察這很多個值&#xff0c;去判斷按鈕該顯示什么顏色&#xff0c;比如Button().…

全鏈條自研可控|江波龍汽車存儲“雙輪驅動”體系亮相MemoryS 2025

3月12日&#xff0c;MemoryS 2025在深圳盛大開幕&#xff0c;匯聚了存儲行業的頂尖專家、企業領袖以及技術先鋒&#xff0c;共同探討存儲技術的未來發展方向及其在商業領域的創新應用。江波龍董事長、總經理蔡華波先生受邀出席&#xff0c;并發表了題為《存儲商業綜合創新》的主…

基于Python+SQLite實現校園信息化統計平臺

一、項目基本情況 概述 本項目以清華大學為預期用戶&#xff0c;作為校內信息化統計平臺進行服務&#xff0c;建立網頁端和移動端校內信息化統計平臺&#xff0c;基于Project_1的需求實現。 本項目能夠滿足校內學生團體的幾類統計需求&#xff0c;如活動報名、實驗室招募、多…

(每日一題) 力扣 2418. 按身高排序

文章目錄 &#x1f984; LeetCode 2418.按身高排序&#xff5c;雙解法對比與下標排序的精妙設計&#x1f4dd; 問題描述&#x1f4a1; 解法思路分析方法一&#xff1a;Pair打包法&#xff08;直接排序&#xff09;方法二&#xff1a;下標排序法&#xff08;當前實現&#xff09…

計算機畢業設計:ktv點歌系統

ktv點歌系統mysql數據庫創建語句ktv點歌系統oracle數據庫創建語句ktv點歌系統sqlserver數據庫創建語句ktv點歌系統springspringMVChibernate框架對象(javaBean,pojo)設計ktv點歌系統springspringMVCmybatis框架對象(javaBean,pojo)設計 ktv點歌系統mysql數據庫版本源碼&#xf…

Deepin通過二進制方式升級部署高版本 Docker

一、背景&#xff1a; 在Deepin系統中通過二進制方式升級部署高版本 Docker&#xff0c;下面將詳細介紹二進制方式升級部署高版本 Docker 的具體步驟。 二、操作步驟 1.根據需求下載二進制文件&#xff0c;下載地址如下&#xff1a; https://mirrors.tuna.tsinghua.e…

2025年Draw.io最新版本下載安裝教程,附詳細圖文

2025年Draw.io最新版本下載安裝教程&#xff0c;附詳細圖文 大家好&#xff0c;今天給大家介紹一款非常實用的流程圖繪制軟件——Draw.io。不管你是平時需要設計流程圖、繪制思維導圖&#xff0c;還是制作架構圖&#xff0c;甚至是簡單的草圖&#xff0c;它都能幫你輕松搞定。…

道路運輸安全員考試備考:循序漸進,穩步提升

備考道路運輸安全員考試是一個循序漸進的過程&#xff0c;需要穩步提升自己的知識和能力。? 第一階段是基礎鞏固階段。這一階段要以教材為核心&#xff0c;全面系統地學習各個知識板塊。從道路運輸法規開始&#xff0c;逐章逐節地學習&#xff0c;理解每一條法規的含義和適用…

滑動窗口(2)——哈希表輔助的滑動窗口算法

歡迎來到博主的專欄&#xff1a;算法解析 博主ID&#xff1a;代碼小豪 文章目錄 leetcode438——找到字符串中所有字母異位詞題目解析算法原理題解代碼 leetcode30——串聯所有單詞的子串題目解析算法原理題解代碼 leetcode438——找到字符串中所有字母異位詞 題目解析 異位詞…

Deepseek -> 如何寫 Dockerfile

嗯&#xff0c;用戶問的是如何制作Dockerfile&#xff0c;我得先理清楚步驟。首先&#xff0c;Dockerfile的基礎結構是什么&#xff1f;應該從基礎鏡像開始&#xff0c;對吧&#xff1f;比如FROM指令。然后可能需要設置工作目錄&#xff0c;用WORKDIR。接著復制文件&#xff0c…

RabbitMQ重復消費如何解決

消息重復消費的原因 生產者重試&#xff1a;網絡波動導致生產者未收到 Broker 確認&#xff0c;重復發送消息。消費者失敗&#xff1a;消費者處理消息后未發送 ACK&#xff0c;消息重新入隊。集群故障轉移&#xff1a;主節點宕機&#xff0c;未確認消息被重新投遞。 解決方案 …

Node-RED基礎1

目錄 一、概述二、安裝三、基操四、通訊五、數據六、節點七、 應用END 一、概述 Rode-Red是什么&#xff1f; 基于Node.js的物聯網開發工具&#xff0c;做API、通訊&#xff1b;提供了一些基本的監控功能&#xff0c;可在編輯器界面中查看節點的運行狀態、消息流量等信息。通…

java登神之階之順序表

一、了解List接口 在Java中&#xff0c;List接口是一個非常重要的集合框架接口&#xff0c;它繼承自Collection接口&#xff08;Collection接口繼承Iterable接口&#xff09;。List接口定義了一個有序集合&#xff0c;允許我們存儲元素集合。并且可以根據元素的索引來訪問集合中…