Redis架構之防雪崩設計:網站不宕機背後的兵法
一、緩存穿透預防及優化
緩存穿透是指查詢一個根本不存在的數據,緩存層和存儲層都不會命中,但是出於容錯的考慮,如果從存儲層查不到數據則不寫入緩存層,如圖 11-3 所示整個過程分為如下 3 步:
緩存層不命中
存儲層不命中,所以不將空結果寫回緩存
返回空結果
緩存穿透將導致不存在的數據每次請求都要到存儲層去查詢,失去了緩存保護後端存儲的意義。
緩存穿透模型
緩存穿透問題可能會使後端存儲負載加大,由於很多後端存儲不具備高並發性,甚至可能造成後端存儲宕掉。通常可以在程序中分別統計總調用數、緩存層命中數、存儲層命中數,如果發現大量存儲層空命中,可能就是出現了緩存穿透問題。
造成緩存穿透的基本有兩個。第一,業務自身代碼或者數據出現問題,第二,一些惡意攻擊、爬蟲等造成大量空命中,下面我們來看一下如何解決緩存穿透問題。
緩存穿透的解決方法
1)緩存空對象
如下圖所示,當第 2 步存儲層不命中後,仍然將空對象保留到緩存層中,之後再訪問這個數據將會從緩存中獲取,保護了後端數據源。
緩存空值應對穿透問題
緩存空對象會有兩個問題:
第一,空值做了緩存,意味著緩存層中存了更多的鍵,需要更多的內存空間 ( 如果是攻擊,問題更嚴重 ),比較有效的方法是針對這類數據設置一個較短的過期時間,讓其自動剔除。
第二,緩存層和存儲層的數據會有一段時間窗口的不一致,可能會對業務有一定影響。例如過期時間設置為 5 分鐘,如果此時存儲層添加了這個數據,那此段時間就會出現緩存層和存儲層數據的不一致,此時可以利用消息系統或者其他方式清除掉緩存層中的空對象。
下面給出了緩存空對象的實現偽代碼:
2)布隆過濾器攔截
如下圖所示,在訪問緩存層和存儲層之前,將存在的 key 用布隆過濾器提前保存起來,做第一層攔截。例如: 一個個性化推薦系統有 4 億個用戶 ID,每個小時演算法工程師會根據每個用戶之前歷史行為做出來的個性化放到存儲層中,但是最新的用戶由於沒有歷史行為,就會發生緩存穿透的行為,為此可以將所有有個性化推薦數據的用戶做成布隆過濾器。如果布隆過濾器認為該用戶 ID 不存在,那麼就不會訪問存儲層,在一定程度保護了存儲層。
開發提示:
有關布隆過濾器的相關知識,可以參考: https://en.wikipedia.org/wiki/Bloom_filter
可以利用 Redis 的 Bitmaps 實現布隆過濾器,GitHub 上已經開源了類似的方案,讀者可以進行參考:
https://github.com/erikdubbelboer/Redis-Lua-scaling-bloom-filter
使用布隆過濾器應對穿透問題
這種方法適用於數據命中不高,數據相對固定實時性低(通常是數據集較大)的應用場景,代碼維護較為複雜,但是緩存空間佔用少。
兩種方案對比
前面介紹了緩存穿透問題的兩種解決方法 ( 實際上這個問題是一個開放問題,有很多解決方法 ),下面通過下表從適用場景和維護成本兩個方面對兩種方案進行分析。
緩存空對象和布隆過濾器方案對比
二、緩存雪崩問題優化
從下圖可以很清晰出什麼是緩存雪崩:由於緩存層承載著大量請求,有效的保護了存儲層,但是如果緩存層由於某些原因整體不能提供服務,於是所有的請求都會達到存儲層,存儲層的調用量會暴增,造成存儲層也會掛掉的情況。緩存雪崩的英文原意是 stampeding herd(奔逃的野牛),指的是緩存層宕掉後,流量會像奔逃的野牛一樣,打向後端存儲。
緩存層不可用引起的雪崩
預防和解決緩存雪崩問題,可以從以下三個方面進行著手。
1)保證緩存層服務高可用性。
和飛機都有多個引擎一樣,如果緩存層設計成高可用的,即使個別節點、個別機器、甚至是機房宕掉,依然可以提供服務,例如前面介紹過的 Redis Sentinel 和 Redis Cluster 都實現了高可用。
2)依賴隔離組件為後端限流並降級。
無論是緩存層還是存儲層都會有出錯的概率,可以將它們視同為資源。作為並發量較大的系統,假如有一個資源不可用,可能會造成線程全部 hang 在這個資源上,造成整個系統不可用。降級在高並發系統中是非常正常的:比如推薦服務中,如果個性化推薦服務不可用,可以降級補充熱點數據,不至於造成前端頁面是開天窗。
在實際項目中,我們需要對重要的資源 ( 例如 Redis、 MySQL、 Hbase、外部介面 ) 都進行隔離,讓每種資源都單獨運行在自己的線程池中,即使個別資源出現了問題,對其他服務沒有影響。但是線程池如何管理,比如如何關閉資源池,開啟資源池,資源池閥值管理,這些做起來還是相當複雜的,這裡推薦一個 Java 依賴隔離工具 Hystrix(https://github.com/Netflix/Hystrix),如下圖所示。
Hystrix 是解決依賴隔離的利器,但是該內容已經超出本書的範圍,同時只適用於 Java 應用,所以這裡不會詳細介紹。
Hystrix 示意圖
3)提前演練。在項目上線前,演練緩存層宕掉後,應用以及後端的負載情況以及可能出現的問題,在此基礎上做一些預案設定。
三、緩存熱點 key 重建優化
開發人員使用緩存 + 過期時間的策略既可以加速數據讀寫,又保證數據的定期更新,這種模式基本能夠滿足絕大部分需求。但是有兩個問題如果同時出現,可能就會對應用造成致命的危害:
當前 key 是一個熱點 key( 例如一個熱門的娛樂新聞),並發量非常大。
重建緩存不能在短時間完成,可能是一個複雜計算,例如複雜的 SQL、多次 IO、多個依賴等。
在緩存失效的瞬間,有大量線程來重建緩存 ( 如下圖),造成後端負載加大,甚至可能會讓應用崩潰。
熱點 key 失效後大量線程重建緩存
要解決這個問題也不是很複雜,但是不能為了解決這個問題給系統帶來更多的麻煩,所以需要制定如下目標:
減少重建緩存的次數
數據儘可能一致
較少的潛在危險
1)互斥鎖 (mutex key)
此方法只允許一個線程重建緩存,其他線程等待重建緩存的線程執行完,重新從緩存獲取數據即可,整個過程如圖 11-17。
使用互斥鎖重建緩存
下面代碼使用 Redis 的 setnx 命令實現上述功能。
(1) 從 Redis 獲取數據,如果值不為空,則直接返回值,否則執行 (2.1) 和 (2.2)。
(2) 如果 set(nx 和 ex) 結果為 true,說明此時沒有其他線程重建緩存,那麼當前線程執行緩存構建邏輯。
(2.2) 如果 setnx(nx 和 ex) 結果為 false,說明此時已經有其他線程正在執行構建緩存的工作,那麼當前線程將休息指定時間 ( 例如這裡是 50 毫秒,取決於構建緩存的速度 ) 後,重新執行函數,直到獲取到數據。
2)永遠不過期
「永遠不過期」包含兩層意思:
從緩存層面來看,確實沒有設置過期時間,所以不會出現熱點 key 過期後產生的問題,也就是「物理」不過期。
從功能層面來看,為每個 value 設置一個邏輯過期時間,當發現超過邏輯過期時間後,會使用單獨的線程去構建緩存。
整個過程如下圖所示:
" 永遠不過期 " 策略
從實戰看,此方法有效杜絕了熱點 key 產生的問題,但唯一不足的就是重構緩存期間,會出現數據不一致的情況,這取決於應用方是否容忍這種不一致。下面代碼使用 Redis 進行模擬:
作為一個並發量較大的應用,在使用緩存時有三個目標:第一,加快用戶訪問速度,提高用戶體驗。第二,降低後端負載,減少潛在的風險,保證系統平穩。第三,保證數據「儘可能」及時更新。下面將按照這三個維度對上述兩種解決方案進行分析。
互斥鎖 (mutex key):這種方案思路比較簡單,但是存在一定的隱患,如果構建緩存過程出現問題或者時間較長,可能會存在死鎖和線程池阻塞的風險,但是這種方法能夠較好的降低後端存儲負載並在一致性上做的比較好。
" 永遠不過期 ":這種方案由於沒有設置真正的過期時間,實際上已經不存在熱點 key 產生的一系列危害,但是會存在數據不一致的情況,同時代碼複雜度會增大。
兩種解決方法對比如下表所示。
兩種熱點 key 的解決方法
本文列舉了緩存設計中最關鍵的 3 個問題,節選自機械工業出版社《Redis開發與運維》第 11 章。由機械工業出版社華章IT授權。基於移動閱讀的考慮,部分內容進行了排版調整,想了解緩存設計及 Redis 開發與運維全部詳細內容,可以在各大書店購買。


※容器與持久化存儲:容器的開源分布式存儲方案選型
※模塊化還是微服務-為什麼說大部分團隊微服務化都走入了陷阱
※Protobuf有沒有比JSON快5倍?用代碼來擊破pb性能神話
TAG:高可用架構 |
※韓國Bigbang吸毒 性醜聞 人命官司,人氣組合要雪崩?
※DOTA2邀請賽中國戰隊雪崩 NB不敵Col入敗者組
※2ch:栃木雪崩 死亡的8人均未攜帶可以發送位置信息的「Beacon」設備
※大一學生雪崩:韓春雨的NgAgo只是一場急功近利的騙局
※讓路新旗艦,華為Mate 9價格雪崩不止!網友:真拼了!
※NASA探測器拍到正在發生中的火星雪崩
※敘利亞軍隊關門打狗 大迂迴切斷補給線!叛軍雪崩般潰退
※重磅利空:機構底部漲停板集體拋售?該股下周或雪崩式下跌
※樂福攻防兩端全被完爆!他打中鋒對手全是全明星,開局就雪崩!
※標緻、雪鐵龍、DS銷量均雪崩 PSA集團在中國還能存活多久?
※[v] 劫後餘生:滑雪者遭遇雪崩
※西方武器遭「雪崩」式慘敗,俄羅斯軍火商笑了:你們終於也有今天
※斯大林格勒戰役,雪崩般的死亡改變了戰爭的走向
※新阿爾法GO橫掃60人類棋手:「雪崩」無法逆轉
※[視頻]鞋帶為什麼總是莫名鬆開:「雪崩效應」勢不可擋
※國外消防員雪崩救援中意外發現三隻小白狗,萌化消防員的心
※火車進站激起大片雪浪 宛若雪崩一般震撼
※若樓市雪崩,房奴們將插翅難逃!
※西藏哨所養老鼠:會作揖翻跟頭 幫官兵規避雪崩