程序員修神之路--聽說你會緩存?
- 緩存是什么?(沒聽說過你可以走了)
- 哪些場景需要用到緩存呢?
- 緩存可以分類嗎?
- 緩存的實現方式有哪些?
緩存
緩存也被稱為Cache,本質上是數據交換的一段緩沖區,也可以稱為一種存儲數據的組件,緩存主要用于減小數據交換雙方速度不匹配的問題。
緩存在計算機世界里是一個常見并且不可忽視的一個重要因素,它幾乎遍布于你所知的各個領域。例如cpu的一級緩存,二級緩存;瀏覽器的緩存等。我們在使用緩存的時候要清除的認識到緩存的數據是有有效期的,也就是說可能隨時會消失。有的同學會說了,類似redis這些組件都提供了數據持久化的功能,這樣數據就不會消失了。至于這個問題其實我想說兩點:
- 當組件提供了持久化功能的時候,必然會發生磁盤的IO操作,而磁盤IO的操作必然會大大降低緩存組件的性能,那緩存的價值還有嗎?
- 緩存的數據在時間定義上是一種臨時性的數據,如果做了持久化,這種臨時性的意義就不存在了,而且還占用了磁盤的存儲空間
緩存最常見的存儲介質是內存,但這并不意味只有內存可以存儲緩存數據,這也是初學者經常會犯的錯誤。緩存的作用是提供高速的讀寫功能,所以如果你的設備足夠快,理論上都可以作為緩存使用,比如現在的SSD,在一些性能不太嚴格和敏感的場景下就可以作為存儲緩存數據的介質,至于計算機的各種硬件之間的速度差距可以參考之前的文章:
高并發下為什么更喜歡進程內緩存
緩存應用場景
“從理論上來說,任何需要提高訪問速度的環節都可以加入緩存
但是系統加入緩存模塊會在一定程度上增加系統的復雜度,所以在是否引入緩存的問題上,需要根據業務場景來平衡。一般符合以下幾種特征的數據可以考慮引入緩存模塊:
數據很少變動
這類數據最適合緩存的應用場景,因為它基本不涉及到負責的緩存更新操作,所以只要將其加載到緩存中即可。最具有代表性的像網站用到的js,css等這些靜態資源,用戶登錄之后生成的session信息等。
說到數據很少變動,不得不提CDN這種服務了,很多大型網站都會利用CDN來加速一些不變資源的訪問速度,比如一些圖片,視頻等。由于用戶訪問這些資源的本源需要跨越多個主干網,在速度上較慢,而CDN恰恰彌補了這個缺陷,所以這里也可以把CDN看成是一種緩存的服務。
熱點數據
這種數據是我們平時開發中要加緩存的主要原因數據,最有可能導致系統癱瘓的也是這種數據。它最大的特點是發生時間不確定,流量峰值不確定。大家可能還依稀記得微博因為兩個明星出軌而掛掉的事件,雖然微博的系統架構后來經過改造可以同時抗住N個明星出軌,但是在不確定這個因素上依然無能為力。
熱點數據的緩存并不容易設計,因為它帶有單點屬性,什么意思呢?假設我們的緩存服務器有100個節點,這個時候發生了某個熱點新聞,而這個熱點新聞的緩存在0號節點,大量的請求會被路由到0號節點,很有可能會導致0號節點垮掉,如果0號節點垮掉,基于故障轉移策略,流量瞬間會轉移到另外一個節點,然后這個節點會垮掉,以此類推.....緩存雖然提高了系統的整體吞吐量,但是在應對有針對性的流量高峰的時候需要單獨針對。這其實也是分布式系統要解決的問題,既然一個節點扛不住流量高峰,系統可以設計多個節點一起來抗,至于以上的熱點數據場景,最簡單粗暴的方式就是緩存副本,一份緩存數據會存在多份副本,類似于MySQL的讀寫分離方案,多份副本同時提供讀取操作。
耗時操作
某些計算代價或者獲取代價很大的數據在特定的條件下也適合進行緩存。為什么要加特定條件呢?如果系統對這些數據的一致性有著嚴格的要求,而且會頻繁的變動,雖然獲取數據代價比較大,但是你也要充分考慮緩存帶來的副作用。像我們最常用的報表服務,一般生成報表都比較耗時,如果報表的數據是相對穩定的,那我們就可以考慮用緩存來提高系統的性能。
緩存的淘汰
存儲緩存的設備限制了緩存是有大小限制的,如果以16G內存來存儲緩存,那緩存的上限理論上就是16G(但是實際上要小的多),而且緩存帶有時效性,所以當要緩存的數據大于介質容量的時候就需要一種淘汰數據的策略來保證新數據能正常被緩存。
最好的淘汰策略就是把系統不用的數據淘汰出去,但是什么數據是無用數據,這是策略的難點所在,基于用戶行為的不確定性,這種數據所以很難用程序去預測。鑒于系統的常規理論,現在主流的有以下幾種淘汰策略:
- LFU(Least Frequently Used):緩存系統會記住每條緩存數據被訪問的頻率,會優先淘汰最不常用的數據。
- LRU(Least Recently Used):緩存系統會記住每條數據最后的訪問時間,會優先淘汰長時間未被訪問的數據
- ARC(Adaptive Replacement Cache):這個緩存算法同時跟蹤記錄LFU和LRU,以及驅逐緩存條目,來獲得可用緩存的最佳使用,它被認為是性能最好的緩存算法之一,介于LRU和LFU之間,能夠記憶效果和自調,當然開發肯定會比較復雜。
- FIFO:基于隊列的原理的淘汰算法,先進先出。這種算法比較簡單,現實中使用比較少是因為這種業務場景比較少。
緩存實現方式
系統中實現緩存的方式大體上可以分為兩種:
進程內緩存
進程內緩存是指緩存和應用程序在同一個進程內,在獲取緩存數據的時候不需要跨越網絡,所以進程內緩存是訪問速度最快的一種方式。
進程內緩存一般用在單機或者小型系統中,但是,在整體架構實現了一致性的前提下,也可用于大型系統,什么意思呢?舉個栗子:在多個服務器節點的情況下,假如用戶A的信息緩存在0號節點,如果有一種機制能保證用戶A的所有請求都只會到達0號節點,這個時候利用進程內緩存就完全沒有問題。
進程外緩存
顧名思義,進程外緩存的意思是緩存數據和應用程序是隔離的,位于不同的進程內。當然這里又可以把進程外緩存劃分為單機版和分布式版本,單機版本這里就不多說了,會存在單機故障問題。
進程外的分布式版本通常被稱為分布式緩存,是基于分布式理論的一種架構模式。它突破了單機緩存的容量限制和單機故障問題,雖然在訪問速度上比進程內緩存要慢很多,但是相比較磁盤IO操作要快的多,所以現在很多大型系統都喜歡用分布式緩存來提高性能。像用的最多的Redis在3.0版本之后就提供了集群方案。
寫在最后
在面對緩存帶給系統的優勢之后,也要注意到緩存也會有一些不足。
- 緩存和數據源的一致性問題
- 緩存命中問題
- 緩存的雪崩穿透問題
- 緩存的并發競爭問題
- 緩存適合讀多寫少的系統
- 引入緩存組件會給系統設計帶來一定的復雜度
- 緩存會加大運維的成功以及排查bug的成本
雖然緩存帶來了不少問題,但是相比較緩存帶給系統性能的提升是毋庸置疑的。我們在設計一個高并發系統的時候,緩存已經成為了一種必備設計,在正確設計了緩存各種策略之后,才能最大發揮緩存的優勢。
本文轉載自微信公眾號「架構師修行之路」,可以通過以下二維碼關注。轉載本文請聯系架構師修行之路公眾號。