天天看點

【高并發】高并發環境下建構緩存服務需要注意哪些問題?我和阿裡P9聊了很久!

緩存特征

(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 節點,添加了後,發現因為連接配接頻率導緻的問題,仍然存在,并沒有好轉,稱之為”無底洞現象”