mysql数据库雪崩_缓存与数据库一致性之三:缓存穿透、缓存雪崩、key重建方案...
緩存穿透是指查詢一個根本不存在的數(shù)據(jù),緩存層和存儲層都不會命中,但是出于容錯的考慮,如果從存儲層查不到數(shù)據(jù)則不寫入緩存層,如圖 11-3 所示整個過程分為如下 3 步:
緩存層不命中
存儲層不命中,所以不將空結(jié)果寫回緩存
返回空結(jié)果
緩存穿透將導致不存在的數(shù)據(jù)每次請求都要到存儲層去查詢,失去了緩存保護后端存儲的意義。
? 圖-1:緩存穿透模型?緩存穿透問題可能會使后端存儲負載加大,由于很多后端存儲不具備高并發(fā)性,甚至可能造成后端存儲宕掉。通常可以在程序中分別統(tǒng)計總調(diào)用數(shù)、緩存層命中數(shù)、存儲層命中數(shù),如果發(fā)現(xiàn)大量存儲層空命中,可能就是出現(xiàn)了緩存穿透問題。?造成緩存穿透的基本有兩個。第一,業(yè)務(wù)自身代碼或者數(shù)據(jù)出現(xiàn)問題,第二,一些惡意攻擊、爬蟲等造成大量空命中,下面我們來看一下如何解決緩存穿透問題。
二、緩存穿透的解決方法
1)緩存空對象
如下圖所示,當?shù)?2 步存儲層不命中后,仍然將空對象保留到緩存層中,之后再訪問這個數(shù)據(jù)將會從緩存中獲取,保護了后端數(shù)據(jù)源。
?緩存空對象會有兩個問題:?第一,空值做了緩存,意味著緩存層中存了更多的鍵,需要更多的內(nèi)存空間 ( 如果是攻擊,問題更嚴重 ),比較有效的方法是針對這類數(shù)據(jù)設(shè)置一個較短的過期時間,讓其自動剔除。?第二,緩存層和存儲層的數(shù)據(jù)會有一段時間窗口的不一致,可能會對業(yè)務(wù)有一定影響。例如過期時間設(shè)置為 5 分鐘,如果此時存儲層添加了這個數(shù)據(jù),那此段時間就會出現(xiàn)緩存層和存儲層數(shù)據(jù)的不一致,此時可以利用消息系統(tǒng)或者其他方式清除掉緩存層中的空對象。?下面給出了緩存空對象的實現(xiàn)偽代碼:?
2)布隆過濾器攔截
如下圖所示,在訪問緩存層和存儲層之前,將存在的 key 用布隆過濾器提前保存起來,做第一層攔截。
例如: 一個個性化推薦系統(tǒng)有 4 億個用戶 ID,每個小時算法工程師會根據(jù)每個用戶之前歷史行為做出來的個性化放到存儲層中,但是最新的用戶由于沒有歷史行為,就會發(fā)生緩存穿透的行為,為此可以將所有有個性化推薦數(shù)據(jù)的用戶做成布隆過濾器。如果布隆過濾器認為該用戶 ID 不存在,那么就不會訪問存儲層,在一定程度保護了存儲層。?開發(fā)提示:?有關(guān)布隆過濾器的相關(guān)知識,可以參考:?Bloom Filter(布隆過濾器)的概念和原理
可以利用 Redis 的 Bitmaps 實現(xiàn)布隆過濾器,GitHub 上已經(jīng)開源了類似的方案,讀者可以進行參考:?https://github.com/erikdubbelboer/Redis-Lua-scaling-bloom-filter
?使用布隆過濾器應(yīng)對穿透問題?這種方法適用于數(shù)據(jù)命中不高,數(shù)據(jù)相對固定實時性低(通常是數(shù)據(jù)集較大)的應(yīng)用場景,代碼維護較為復雜,但是緩存空間占用少。
兩種方案對比
前面介紹了緩存穿透問題的兩種解決方法 ( 實際上這個問題是一個開放問題,有很多解決方法 ),下面通過下表從適用場景和維護成本兩個方面對兩種方案進行分析。?緩存空對象和布隆過濾器方案對比
三、緩存雪崩問題優(yōu)化
從下圖可以很清晰出什么是緩存雪崩:由于緩存層承載著大量請求,有效的保護了存儲層,但是如果緩存層由于某些原因整體不能提供服務(wù),于是所有的請求都會達到存儲層,存儲層的調(diào)用量會暴增,造成存儲層也會掛掉的情況。緩存雪崩的英文原意是 stampeding herd(奔逃的野牛),指的是緩存層宕掉后,流量會像奔逃的野牛一樣,打向后端存儲。
?緩存層不可用引起的雪崩?預(yù)防和解決緩存雪崩問題,可以從以下三個方面進行著手。?1)保證緩存層服務(wù)高可用性。?和飛機都有多個引擎一樣,如果緩存層設(shè)計成高可用的,即使個別節(jié)點、個別機器、甚至是機房宕掉,依然可以提供服務(wù),例如前面介紹過的 Redis Sentinel 和 Redis Cluster 都實現(xiàn)了高可用。?2)依賴隔離組件為后端限流并降級。?無論是緩存層還是存儲層都會有出錯的概率,可以將它們視同為資源。作為并發(fā)量較大的系統(tǒng),假如有一個資源不可用,可能會造成線程全部 hang 在這個資源上,造成整個系統(tǒng)不可用。降級在高并發(fā)系統(tǒng)中是非常正常的:比如推薦服務(wù)中,如果個性化推薦服務(wù)不可用,可以降級補充熱點數(shù)據(jù),不至于造成前端頁面是開天窗。?在實際項目中,我們需要對重要的資源 ( 例如 Redis、 MySQL、 Hbase、外部接口 ) 都進行隔離,讓每種資源都單獨運行在自己的線程池中,即使個別資源出現(xiàn)了問題,對其他服務(wù)沒有影響。但是線程池如何管理,比如如何關(guān)閉資源池,開啟資源池,資源池閥值管理,這些做起來還是相當復雜的,這里推薦一個 Java 依賴隔離工具 Hystrix(https://github.com/Netflix/Hystrix),如下圖所示。?Hystrix 是解決依賴隔離的利器,但是該內(nèi)容已經(jīng)超出本書的范圍,同時只適用于 Java 應(yīng)用,所以這里不會詳細介紹。?
?Hystrix 示意圖?3)提前演練。在項目上線前,演練緩存層宕掉后,應(yīng)用以及后端的負載情況以及可能出現(xiàn)的問題,在此基礎(chǔ)上做一些預(yù)案設(shè)定。
四、緩存熱點 key 重建優(yōu)化
開發(fā)人員使用緩存 + 過期時間的策略既可以加速數(shù)據(jù)讀寫,又保證數(shù)據(jù)的定期更新,這種模式基本能夠滿足絕大部分需求。但是有兩個問題如果同時出現(xiàn),可能就會對應(yīng)用造成致命的危害:
當前 key 是一個熱點 key( 例如一個熱門的娛樂新聞),并發(fā)量非常大。
重建緩存不能在短時間完成,可能是一個復雜計算,例如復雜的 SQL、多次 IO、多個依賴等。
在緩存失效的瞬間,有大量線程來重建緩存 ( 如下圖),造成后端負載加大,甚至可能會讓應(yīng)用崩潰。
?熱點 key 失效后大量線程重建緩存?要解決這個問題也不是很復雜,但是不能為了解決這個問題給系統(tǒng)帶來更多的麻煩,所以需要制定如下目標:
減少重建緩存的次數(shù)
數(shù)據(jù)盡可能一致
較少的潛在危險?1)互斥鎖 (mutex key)此方法只允許一個線程重建緩存,其他線程等待重建緩存的線程執(zhí)行完,重新從緩存獲取數(shù)據(jù)即可,整個過程如圖 :
使用互斥鎖重建緩存?下面代碼使用 Redis 的 setnx 命令實現(xiàn)上述功能。?
(1) 從 Redis 獲取數(shù)據(jù),如果值不為空,則直接返回值,否則執(zhí)行 (2.1) 和 (2.2)。?(2) 如果 set(nx 和 ex) 結(jié)果為 true,說明此時沒有其他線程重建緩存,那么當前線程執(zhí)行緩存構(gòu)建邏輯。?(2.2) 如果 setnx(nx 和 ex) 結(jié)果為 false,說明此時已經(jīng)有其他線程正在執(zhí)行構(gòu)建緩存的工作,那么當前線程將休息指定時間 ( 例如這里是 50 毫秒,取決于構(gòu)建緩存的速度 ) 后,重新執(zhí)行函數(shù),直到獲取到數(shù)據(jù)。?2)永遠不過期“永遠不過期”包含兩層意思:?從緩存層面來看,確實沒有設(shè)置過期時間,所以不會出現(xiàn)熱點 key 過期后產(chǎn)生的問題,也就是“物理”不過期。?從功能層面來看,為每個 value 設(shè)置一個邏輯過期時間,當發(fā)現(xiàn)超過邏輯過期時間后,會使用單獨的線程去構(gòu)建緩存。?整個過程如下圖所示:?
” 永遠不過期 ” 策略?從實戰(zhàn)看,此方法有效杜絕了熱點 key 產(chǎn)生的問題,但唯一不足的就是重構(gòu)緩存期間,會出現(xiàn)數(shù)據(jù)不一致的情況,這取決于應(yīng)用方是否容忍這種不一致。下面代碼使用 Redis 進行模擬:?
作為一個并發(fā)量較大的應(yīng)用,在使用緩存時有三個目標:第一,加快用戶訪問速度,提高用戶體驗。第二,降低后端負載,減少潛在的風險,保證系統(tǒng)平穩(wěn)。第三,保證數(shù)據(jù)“盡可能”及時更新。下面將按照這三個維度對上述兩種解決方案進行分析。?互斥鎖 (mutex key):這種方案思路比較簡單,但是存在一定的隱患,如果構(gòu)建緩存過程出現(xiàn)問題或者時間較長,可能會存在死鎖和線程池阻塞的風險,但是這種方法能夠較好的降低后端存儲負載并在一致性上做的比較好。” 永遠不過期 “:這種方案由于沒有設(shè)置真正的過期時間,實際上已經(jīng)不存在熱點 key 產(chǎn)生的一系列危害,但是會存在數(shù)據(jù)不一致的情況,同時代碼復雜度會增大。兩種解決方法對比如下表所示。?兩種熱點 key 的解決方法?
轉(zhuǎn)載自微信:高可用架構(gòu)
總結(jié)
以上是生活随笔為你收集整理的mysql数据库雪崩_缓存与数据库一致性之三:缓存穿透、缓存雪崩、key重建方案...的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: mysql视图有哪几种_数据库报表的视图
- 下一篇: java读取文件 16进制_Java对文