緩存雪崩、擊穿、穿透解決方案

用戶的數據一般都是存儲于數據庫,數據庫的數據是落在磁盤上的,磁盤的讀寫速度可以說是計算機里最慢的硬件了。

當用戶的請求,都訪問數據庫的話,請求數量一上來,數據庫很容易就奔潰的了,所以為了避免用戶直接訪問數據庫,會用 Redis 作為緩存層。

因為 Redis 是內存數據庫,我們可以將數據庫的數據緩存在 Redis 里,相當于數據緩存在內存,內存的讀寫速度比硬盤快好幾個數量級,這樣大大提高了系統性能。

圖片

引入了緩存層,就會有緩存異常的三個問題,分別是緩存雪崩、緩存擊穿、緩存穿透

這三個問題也是面試中很常考察的問題,我們不光要清楚地知道它們是怎么發生,還需要知道如何解決它們。

話不多說,發車!

圖片


緩存雪崩

通常我們為了保證緩存中的數據與數據庫中的數據一致性,會給 Redis 里的數據設置過期時間,當緩存數據過期后,用戶訪問的數據如果不在緩存里,業務系統需要重新生成緩存,因此就會訪問數據庫,并將數據更新到 Redis 里,這樣后續請求都可以直接命中緩存。

圖片

那么,當大量緩存數據在同一時間過期(失效)或者 Redis 故障宕機時,如果此時有大量的用戶請求,都無法在 Redis 中處理,于是全部請求都直接訪問數據庫,從而導致數據庫的壓力驟增,嚴重的會造成數據庫宕機,從而形成一系列連鎖反應,造成整個系統崩潰,這就是緩存雪崩的問題。

圖片

可以看到,發生緩存雪崩有兩個原因:

  • 大量數據同時過期;

  • Redis 故障宕機;

不同的誘因,應對的策略也會不同。

大量數據同時過期

針對大量數據同時過期而引發的緩存雪崩問題,常見的應對方法有下面這幾種:

  • 均勻設置過期時間;

  • 互斥鎖;

  • 雙 key 策略;

  • 后臺更新緩存;

1. 均勻設置過期時間

如果要給緩存數據設置過期時間,應該避免將大量的數據設置成同一個過期時間。我們可以在對緩存數據設置過期時間時,給這些數據的過期時間加上一個隨機數,這樣就保證數據不會在同一時間過期。

2. 互斥鎖

當業務線程在處理用戶請求時,如果發現訪問的數據不在 Redis 里,就加個互斥鎖,保證同一時間內只有一個請求來構建緩存(從數據庫讀取數據,再將數據更新到 Redis 里),當緩存構建完成后,再釋放鎖。未能獲取互斥鎖的請求,要么等待鎖釋放后重新讀取緩存,要么就返回空值或者默認值。

實現互斥鎖的時候,最好設置超時時間,不然第一個請求拿到了鎖,然后這個請求發生了某種意外而一直阻塞,一直不釋放鎖,這時其他請求也一直拿不到鎖,整個系統就會出現無響應的現象。

3. 雙 key 策略

我們對緩存數據可以使用兩個 key,一個是主 key,會設置過期時間,一個是備 key,不會設置過期,它們只是 key 不一樣,但是 value 值是一樣的,相當于給緩存數據做了個副本。

當業務線程訪問不到「主 key 」的緩存數據時,就直接返回「備 key 」的緩存數據,然后在更新緩存的時候,同時更新「主 key 」和「備 key 」的數據。

4. 后臺更新緩存

業務線程不再負責更新緩存,緩存也不設置有效期,而是讓緩存“永久有效”,并將更新緩存的工作交由后臺線程定時更新

事實上,緩存數據不設置有效期,并不是意味著數據一直能在內存里,因為當系統內存緊張的時候,有些緩存數據會被“淘汰”,而在緩存被“淘汰”到下一次后臺定時更新緩存的這段時間內,業務線程讀取緩存失敗就返回空值,業務的視角就以為是數據丟失了。

解決上面的問題的方式有兩種。

第一種方式,后臺線程不僅負責定時更新緩存,而且也負責頻繁地檢測緩存是否有效,檢測到緩存失效了,原因可能是系統緊張而被淘汰的,于是就要馬上從數據庫讀取數據,并更新到緩存。

這種方式的檢測時間間隔不能太長,太長也導致用戶獲取的數據是一個空值而不是真正的數據,所以檢測的間隔最好是毫秒級的,但是總歸是有個間隔時間,用戶體驗一般。

第二種方式,在業務線程發現緩存數據失效后(緩存數據被淘汰),通過消息隊列發送一條消息通知后臺線程更新緩存,后臺線程收到消息后,在更新緩存前可以判斷緩存是否存在,存在就不執行更新緩存操作;不存在就讀取數據庫數據,并將數據加載到緩存。這種方式相比第一種方式緩存的更新會更及時,用戶體驗也比較好。

在業務剛上線的時候,我們最好提前把數據緩起來,而不是等待用戶訪問才來觸發緩存構建,這就是所謂的緩存預熱,后臺更新緩存的機制剛好也適合干這個事情。

Redis 故障宕機

針對 Redis 故障宕機而引發的緩存雪崩問題,常見的應對方法有下面這幾種:

  • 服務熔斷或請求限流機制;

  • 構建 Redis 緩存高可靠集群;

1. 服務熔斷或請求限流機制

因為 Redis 故障宕機而導致緩存雪崩問題時,我們可以啟動服務熔斷機制,暫停業務應用對緩存服務的訪問,直接返回錯誤,不用再繼續訪問數據庫,從而降低對數據庫的訪問壓力,保證數據庫系統的正常運行,然后等到 Redis 恢復正常后,再允許業務應用訪問緩存服務。

服務熔斷機制是保護數據庫的正常允許,但是暫停了業務應用訪問緩存服系統,全部業務都無法正常工作

為了減少對業務的影響,我們可以啟用請求限流機制,只將少部分請求發送到數據庫進行處理,再多的請求就在入口直接拒絕服務,等到 Redis 恢復正常并把緩存預熱完后,再解除請求限流的機制。

2. 構建 Redis 緩存高可靠集群

服務熔斷或請求限流機制是緩存雪崩發生后的應對方案,我們最好通過主從節點的方式構建 Redis 緩存高可靠集群

如果 Redis 緩存的主節點故障宕機,從節點可以切換成為主節點,繼續提供緩存服務,避免了由于 Redis 故障宕機而導致的緩存雪崩問題。


緩存擊穿

我們的業務通常會有幾個數據會被頻繁地訪問,比如秒殺活動,這類被頻地訪問的數據被稱為熱點數據。

如果緩存中的某個熱點數據過期了,此時大量的請求訪問了該熱點數據,就無法從緩存中讀取,直接訪問數據庫,數據庫很容易就被高并發的請求沖垮,這就是緩存擊穿的問題。

圖片

可以發現緩存擊穿跟緩存雪崩很相似,你可以認為緩存擊穿是緩存雪崩的一個子集。

應對緩存擊穿可以采取前面說到兩種方案:

  • 互斥鎖方案,保證同一時間只有一個業務線程更新緩存,未能獲取互斥鎖的請求,要么等待鎖釋放后重新讀取緩存,要么就返回空值或者默認值。

  • 不給熱點數據設置過期時間,由后臺異步更新緩存,或者在熱點數據準備要過期前,提前通知后臺線程更新緩存以及重新設置過期時間;


緩存穿透

當發生緩存雪崩或擊穿時,數據庫中還是保存了應用要訪問的數據,一旦緩存恢復相對應的數據,就可以減輕數據庫的壓力,而緩存穿透就不一樣了。

當用戶訪問的數據,既不在緩存中,也不在數據庫中,導致請求在訪問緩存時,發現緩存缺失,再去訪問數據庫時,發現數據庫中也沒有要訪問的數據,沒辦法構建緩存數據,來服務后續的請求。那么當有大量這樣的請求到來時,數據庫的壓力驟增,這就是緩存穿透的問題。

圖片

緩存穿透的發生一般有這兩種情況:

  • 業務誤操作,緩存中的數據和數據庫中的數據都被誤刪除了,所以導致緩存和數據庫中都沒有數據;

  • 黑客惡意攻擊,故意大量訪問某些讀取不存在數據的業務;

應對緩存穿透的方案,常見的方案有三種。

  • 第一種方案,非法請求的限制;

  • 第二種方案,緩存空值或者默認值;

  • 第三種方案,使用布隆過濾器快速判斷數據是否存在,避免通過查詢數據庫來判斷數據是否存在;

第一種方案,非法請求的限制

當有大量惡意請求訪問不存在的數據的時候,也會發生緩存穿透,因此在 API 入口處我們要判斷求請求參數是否合理,請求參數是否含有非法值、請求字段是否存在,如果判斷出是惡意請求就直接返回錯誤,避免進一步訪問緩存和數據庫。

第二種方案,緩存空值或者默認值

當我們線上業務發現緩存穿透的現象時,可以針對查詢的數據,在緩存中設置一個空值或者默認值,這樣后續請求就可以從緩存中讀取到空值或者默認值,返回給應用,而不會繼續查詢數據庫。

第三種方案,使用布隆過濾器快速判斷數據是否存在,避免通過查詢數據庫來判斷數據是否存在。

我們可以在寫入數據庫數據時,使用布隆過濾器做個標記,然后在用戶請求到來時,業務線程確認緩存失效后,可以通過查詢布隆過濾器快速判斷數據是否存在,如果不存在,就不用通過查詢數據庫來判斷數據是否存在。

即使發生了緩存穿透,大量請求只會查詢 Redis 和布隆過濾器,而不會查詢數據庫,保證了數據庫能正常運行,Redis 自身也是支持布隆過濾器的。

那問題來了,布隆過濾器是如何工作的呢?接下來,我介紹下。

布隆過濾器由「初始值都為 0 的位圖數組」和「 N 個哈希函數」兩部分組成。當我們在寫入數據庫數據時,在布隆過濾器里做個標記,這樣下次查詢數據是否在數據庫時,只需要查詢布隆過濾器,如果查詢到數據沒有被標記,說明不在數據庫中。

布隆過濾器會通過 3 個操作完成標記:

  • 第一步,使用 N 個哈希函數分別對數據做哈希計算,得到 N 個哈希值;

  • 第二步,將第一步得到的 N 個哈希值對位圖數組的長度取模,得到每個哈希值在位圖數組的對應位置。

  • 第三步,將每個哈希值在位圖數組的對應位置的值設置為 1;

舉個例子,假設有一個位圖數組長度為 8,哈希函數 3 個的布隆過濾器。

圖片

在數據庫寫入數據 x 后,把數據 x 標記在布隆過濾器時,數據 x 會被 3 個哈希函數分別計算出 3 個哈希值,然后在對這 3 個哈希值對 8 取模,假設取模的結果為 1、4、6,然后把位圖數組的第 1、4、6 位置的值設置為 1。當應用要查詢數據 x 是否數據庫時,通過布隆過濾器只要查到位圖數組的第 1、4、6 位置的值是否全為 1,只要有一個為 0,就認為數據 x 不在數據庫中

布隆過濾器由于是基于哈希函數實現查找的,高效查找的同時存在哈希沖突的可能性,比如數據 x 和數據 y 可能都落在第 1、4、6 位置,而事實上,可能數據庫中并不存在數據 y,存在誤判的情況。

所以,查詢布隆過濾器說數據存在,并不一定證明數據庫中存在這個數據,但是查詢到數據不存在,數據庫中一定就不存在這個數據


總結

緩存異常會面臨的三個問題:緩存雪崩、擊穿和穿透。

其中,緩存雪崩和緩存擊穿主要原因是數據不在緩存中,而導致大量請求訪問了數據庫,數據庫壓力驟增,容易引發一系列連鎖反應,導致系統奔潰。不過,一旦數據被重新加載回緩存,應用又可以從緩存快速讀取數據,不再繼續訪問數據庫,數據庫的壓力也會瞬間降下來。因此,緩存雪崩和緩存擊穿應對的方案比較類似。

而緩存穿透主要原因是數據既不在緩存也不在數據庫中。因此,緩存穿透與緩存雪崩、擊穿應對的方案不太一樣。

我這里整理了表格,你可以從下面這張表格很好的知道緩存雪崩、擊穿和穿透的區別以及應對方案。

圖片

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

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

相關文章

Ansible中的playbook詳解

首先簡單說明一下playbook,playbook是什么呢? 根本上說playbook和shell腳本沒有任何的區別,playbook就像shell一樣,也是把一堆的命令組合起來,然后加入對應條件判斷等等,在shell腳本中是一條一條的命令&am…

【Docker】容器鏡像有哪些特性

首先解釋一下什么是Docker鏡像? Docker鏡像它其實是一個模板,擁有這個模板我們才能創建我們的Docker容器,鏡像里含有啟動 docker 容器所需的文件系統結構及其內容,因此是啟動一個 docker 容器的基礎。docker 鏡像的文件內容以及一…

nginx中的location指令

1、location 介紹 location是Nginx中的塊級指令(block directive),location指令的功能是用來匹配不同的url請求,進而對請求做不同的處理和響應,這其中較難理解的是多個location的匹配順序,本文會作為重點來解釋和說明。 開始之前…

容器底層實現技術Namespace/Cgroup

Docker容器實現原理 Docker容器在實現上是通過namespace技術來進行進程隔離,通過cgroup技術實現容器進程可用資源的限制,當docker啟動一個容器時,實際是創建了多了namespace參數的進程。 Namespace Namespace:命名空間 作用&#…

身體原因 斷更一周

由于眾所周知的原因,博主最近具有發熱、全身乏力、酸痛、干咳等癥狀,已嚴重影響日常的工作學習和博客編寫,所以斷更我將一周,由下周三(即2022年12月21日)恢復更新 更多往期內容可以參考:全網最…

異方差與多重共線性對回歸問題的影響

異方差的檢驗 1.異方差的畫圖觀察 2.異方差的假設檢驗,假設檢驗有兩種,一般用懷特檢驗使用方法在ppt中,課程中也有實驗,是一段代碼。 異方差的解決辦法 多重共線性 多重共線性可能帶來的影響: 多重共線性的檢驗 多重…

如何修改Docker的鏡像源

改或新增/etc/docker/daemon.json 文件 vi/etc/docker/daemon.json 添加需要修改的國內鏡像源鏡像源 { "registry-mirrors":["http://hub-mirror.c.163.com"] } 重啟Docker服務 Systemctl restart docker.service 方法二 修改或新增 /etc/sysconfig…

nginx 的 rewrite 模塊

ngxhttprewrite_module 模塊用來使用正則表達式(PCRE)改變請求的 URI,返回重定向,并有條件地選擇配置。 指令執行順序 首先順序執行 server 塊中的 rewrite 模塊指令,得到 rewrite 后的請求 URI 然后循環執行如下指令…

所有的Python庫

庫名稱簡介 Chardet字符編碼探測器,可以自動檢測文本、網頁、xml的編碼。 colorama主要用來給文本添加各種顏色,并且非常簡單易用。 Prettytable主要用于在終端或瀏覽器端構建格式化的輸出。 difflib,[Python]標準庫,計算文本…

Oracle行轉列語法總結大全

一、decode語法 SELECT deptno, nvl(SUM(decode(job, MANAGER, sal)), 0) s_MANAGER, nvl(SUM(decode(job, ANALYST, sal)), 0) s_ANALYST, nvl(SUM(decode(job, CLERK, sal)), 0) s_CLERK, nvl(SUM(decode(job, PRESIDENT, sal)), 0) s_PRESIDENT, …

Shell 各種符號 之 含義

#!&#xff1a;符號能夠被內核識別成是一個腳本的開始&#xff0c;這一行必須位于腳本的首行 $0&#xff1a;當前腳本的名字 $#&#xff1a;輸入<調用>參數(腳本或函數的位置參數) 的個數&#xff0c;如 NumArg$#&#xff1b;echo"\$#: $#;\$NumArg: $NumArg"…

Nginx的11個執行流程

1 Nginx簡介 Web服務器市場份額 Nginx [engine x] 最初由 Lgor Sysoev 編寫。根據 Netcraft 的數據&#xff0c;到2020年9月&#xff0c;Nginx 服務或代理了25.76&#xff05;站點&#xff0c;市場份額占到了約34.03&#xff05;。 Nginx 被廣泛用作&#xff1a; HTTP服務器…

Nginx的執行階段詳解

在了解nginx的執行階段前&#xff0c;先看一個例子 對echo不熟悉的&#xff0c;可以先看文章Nginx調試必備了解下echo擴展 回到上面這個例子&#xff0c;在server塊中配置這樣的location&#xff0c;你覺得輸出是什么樣子&#xff1f; 按照正常的邏輯&#xff0c;輸出應該是32 …

Docker掛了,數據如何找回

docker在實際使用中&#xff0c;讓運維人員詬病的&#xff0c;除了安全問題外&#xff0c;大概就是數據的問題了 很多人在初用docker的時候&#xff0c;很多時候都忘記或不知道docker中需要保留的數據需要掛載到宿主機文件夾到容器內部對應目錄&#xff08;當然除了掛載宿主機目…

TCP總結

TCP這些東西&#xff0c;基本每個程序猿都或多或少是掌握的了。雖然感覺在實際開發中沒有什么用武之處&#xff0c;但&#xff0c;面試他要問啊 而最近大家伙過完年&#xff0c;也都在準備春招&#xff0c;我也一樣。閱讀了一些okHttp源碼之后&#xff0c;又屁顛屁顛地跑回來重…

Shell 中各種括號的作用

一、小括號&#xff0c;圓括號&#xff08;&#xff09; 1、單小括號 () ①命令組。括號中的命令將會新開一個子shell順序執行&#xff0c;所以括號中的變量不能夠被腳本余下的部分使用。括號中多個命令之間用分號隔開&#xff0c;最后一個命令可以沒有分號&#xff0c;各命令和…

linux基線核查腳本

#!/bin/bash #version 2.0cat <<EOF ************************************************************************************* ***** linux基線檢查腳本 ***** ************************************************************************************* *…

LDAP命令介紹---dsreplication

可以使用此實用程序來配置服務器之間的復制, 以使服務器數據保持同步。要正確進行復制, 必須先使用 enable 子命令啟用復制, 然后再使用 initialize 子命令將一個服務器的內容初始化為另一個服務器的內容用法: dsreplication {子命令} {選項}可用子命令:disable對指定服務器中…

LDAP命令介紹---dsconfig

此實用程序可用于定義目錄服務器的基本配置 用法: dsconfig {子命令} {選項} 獲取子命令用法列表: --help-distribution 顯示與 分發 相關的子命令 --help-general-configuration 顯示與 一般配置 相關的子命令 --help-integration 顯示與 集成 相關的子命令 --…

LDAP命令介紹---dsconfig子命令----help-distribution顯示與分發相關的子命令

此實用程序可用于定義目錄服務器的基本配置用法: dsconfig {子命令} {選項}可用子命令:create-distribution-algorithm創建分發算法create-distribution-partition創建分發分區create-workflow-element --type distribution創建分發工作流元素create-global-index創建全局索引…