寫在前面
周末,跟阿里的一個朋友(去年晉升為P9了)聊了很久,聊的內容幾乎全是技術,當然了,兩個技術男聊得最多的話題當然就是技術了。從基礎到架構,從算法到AI,無所不談。中間又穿插着不少天馬行空的想象,雖然現在看起來不太實際,但是隨着技術的進步,相信五年、十年之后都會實現的。
不知道是誰提起了在高並發環境下如何構建緩存服務,結果一路停不下來了!!
緩存特征
(1)命中率:命中數/(命中數+沒有命中數)
(2)最大元素(空間):代表緩存中可以存放的最大元素的數量,一旦緩存中元素的數量超過這個值,或者緩存數據所占的空間超過了最大支持的空間,將會觸發緩存清空策略。根據不同的場景,合理設置最大元素(空間)的值,在一定程度上可以提高緩存的命中率,從而更有效的使用緩存。
(3)清空策略:FINO(先進先出)、LFU(最少使用)、LRU(最近最少使用)、過期時間、隨機等。
- FINO(先進先出):最先進入緩存的數據,在緩存空間不夠或超出最大元素限制的情況下,會優先被清除掉,以騰出新的空間來接收新的數據。這種策略的算法主要是比較緩存元素的創建時間,在數據實時性較高的場景下,可以選擇這種策略,優先保證最新策略可用。
- LFU(最少使用):無論元素是否過期,根據元素的被使用次數來判斷,清除使用次數最少的元素來釋放空間。算法主要是比較元素的命中次數,在保證高頻數據有效的場景下,可以選擇這種策略。
- LRU(最近最少使用):無論元素是否過期,根據元素最后一次被使用的時間戳,清除最遠使用時間戳的元素,釋放空間。算法主要是比較元素最近一次被獲取的時間,在熱點數據場景下,可以選擇這種策略。
過期時間:根據過期時間判斷,清理過期時間最長的元素,或者清理最近要過期的元素。
緩存命中率影響因素
(1)業務場景和業務需求
緩存往往適合讀多寫少的場景。業務需求對實時性的要求,直接會影響到緩存的過期時間和更新策略。實時性要求越低,就越適合緩存。在相同Key和相同請求數的情況下,緩存的時間越長,命中率就會越高。
(2)緩存的設計(粒度和策略)
通常情況下,緩存的粒度越小,命中率越高。緩存的更新和命中策略也會影響緩存的命中率,當數據發生變化時,直接更新緩存的值會比移除緩存或使緩存過期的命中率更高。
(3)緩存容量和基礎設施
緩存的容量有限,則容易引起緩存失效和被淘汰(目前多數的緩存框架或中間件都采用了LRU算法)。同時,緩存的技術選型也是至關重要的,比如采用應用內置的本地緩存就比較容易出現單機瓶頸,而采用分布式緩存則畢竟容易擴展。所以需要做好系統容量規划,並考慮是否可擴展。此外,不同的緩存框架或中間件,其效率和穩定性也是存在差異的。
(4)其他因素
當緩存節點發生故障時,需要避免緩存失效並最大程度降低影響,這種特殊情況也是架構師需要考慮的。業內比較典型的做法就是通過一致性Hash算法,或者通過節點冗余的方式。
有些朋友可能會有這樣的理解誤區:既然業務需求對數據時效性要求很高,而緩存時間又會影響到緩存命中率,那么系統就別使用緩存了。其實這忽略了一個重要因素--並發。通常來講,在相同緩存時間和key的情況下,並發越高,緩存的收益會越高,即便緩存時間很短。
提高緩存命中率的方法
從架構師的角度,需要應用盡可能的通過緩存直接獲取數據,並避免緩存失效。這也是比較考驗架構師能力的,需要在業務需求,緩存粒度,緩存策略,技術選型等各個方面去通盤考慮並做權衡。盡可能的聚焦在高頻訪問且時效性要求不高的熱點業務上,通過緩存預加載(預熱)、增加存儲容量、調整緩存粒度、更新緩存等手段來提高命中率。
對於時效性很高(或緩存空間有限),內容跨度很大(或訪問很隨機),並且訪問量不高的應用來說緩存命中率可能長期很低,可能預熱后的緩存還沒來得被訪問就已經過期了。
緩存的分類和應用場景
(1)本地緩存:編程實現(成員變量、局部變量、靜態變量)、Guava Cache
(2)分布式緩存:Memcached、Redis
高並發場景下緩存常見問題
(1)緩存的一致性
更新數據庫成功——更新緩存失敗
更新緩存成功——更新數據庫失敗
更新數據庫成功——淘汰緩存失敗
淘汰緩存成功——更新數據庫失敗
(2)緩存並發
並發時請求緩存時已過期或者沒有命中或者更新的情況下有大量的請求訪問數據庫。
解決辦法:在緩存更新或者過期的情況下,先嘗試獲取到lock,當更新完成后,嘗試釋放鎖,其他的請求只需要犧牲一定的等待時間
(3)緩存穿透
在高並發的場景下,如果某一個key被高並發的訪問沒有被命中,出於對容錯性的考慮會嘗試從后端的數據庫獲取,從而導致大量的請求訪問了數據庫,主要是當key對應的數據為空或者為null的情況下,這就導致數據庫中並發的執行了很多不必要的查詢操作。從而導致了巨大的沖擊和壓力。
解決方法:
緩存空對象:對查詢結果為空的對象也進行緩存,如果是集合可以緩存一個空的集合,而不是null,如果是單個對象可以通過字段標識來區分,需要保證緩存數據的時效性(實現相對簡單),適合命中不高但可能會頻繁更新的數據。
單獨過濾處理:對所有可能對應數據為空的key進行統一的存放,並在請求前做攔截(實現相對復雜),適合命中不高更新不頻繁的數據
(4)緩存顛簸問題
緩存的顛簸問題,有些地方可能被稱為“緩存抖動”,可以看作是一種比“雪崩”更輕微的故障,但是也會在一段時間內對系統造成沖擊和性能影響。一般是由於緩存節點故障導致。業內推薦的做法是通過一致性Hash算法來解決。
(5)緩存雪崩現象
緩存雪崩就是指由於緩存的原因,導致大量請求到達后端數據庫,從而導致數據庫崩潰,整個系統崩潰,發生災難。導致這種現象的原因有很多種,上面提到的“緩存並發”,“緩存穿透”,“緩存顛簸”等問題,其實都可能會導致緩存雪崩現象發生。這些問題也可能會被惡意攻擊者所利用。還有一種情況,例如某個時間點內,系統預加載的緩存周期性集中失效了,也可能會導致雪崩。為了避免這種周期性失效,可以通過設置不同的過期時間,來錯開緩存過期,從而避免緩存集中失效。
從應用架構角度,我們可以通過限流、降級、熔斷等手段來降低影響,也可以通過多級緩存來避免這種災難。
此外,從整個研發體系流程的角度,應該加強壓力測試,盡量模擬真實場景,盡早的暴露問題從而防范。
(6)緩存無底洞現象
該問題由 facebook 的工作人員提出的, facebook 在 2010 年左右,memcached 節點就已經達3000 個,緩存數千 G 內容。他們發現了一個問題---memcached 連接頻率,效率下降了,於是加 memcached 節點,添加了后,發現因為連接頻率導致的問題,仍然存在,並沒有好轉,稱之為”無底洞現象”
重磅福利
關注「 冰河技術 」微信公眾號,后台回復 “設計模式” 關鍵字領取《深入淺出Java 23種設計模式》PDF文檔。回復“Java8”關鍵字領取《Java8新特性教程》PDF文檔。兩本PDF均是由冰河原創並整理的超硬核教程,面試必備!!
好了,今天就聊到這兒吧!別忘了點個贊,給個在看和轉發,讓更多的人看到,一起學習,一起進步!!
寫在最后
如果你覺得冰河寫的還不錯,請微信搜索並關注「 冰河技術 」微信公眾號,跟冰河學習高並發、分布式、微服務、大數據、互聯網和雲原生技術,「 冰河技術 」微信公眾號更新了大量技術專題,每一篇技術文章干貨滿滿!不少讀者已經通過閱讀「 冰河技術 」微信公眾號文章,吊打面試官,成功跳槽到大廠;也有不少讀者實現了技術上的飛躍,成為公司的技術骨干!如果你也想像他們一樣提升自己的能力,實現技術能力的飛躍,進大廠,升職加薪,那就關注「 冰河技術 」微信公眾號吧,每天更新超硬核技術干貨,讓你對如何提升技術能力不再迷茫!