緩存的應用場景
CPU緩存
是位於CPU與內存之間的臨時存儲器,它的容量比內存小的多但是交換速度卻比內存要快得多。(選用存儲介質,提高訪問速度:高速緩存>內存;減少重復量)
瀏覽器緩存
瀏覽器可以緩存一些靜態資源,比如圖片、js、css等,這些都是不常變化的內容,所以沒有必要每次都去請求。(減少網絡IO消耗,提高訪問速度)
CDN緩存
客戶端會先檢查瀏覽器的緩存,若緩存過期則會像CDN發送請求(Request),CDN檢查緩存數據還未過期,那么直接返回響應(Response),只需兩步搞定。但是,CDN緩存過期,那么需要向應用服務器(Web Server)發起請求,獲得新的數據響應,這部分新的數據按一定的緩存策略會選擇是否緩存在CDN中。從下圖可發現,此時數據需要完成1–>3–>4–>2最終返回到瀏覽器端。此處的CDN,(減少網絡IO消耗,提高訪問速度)
數據庫緩存
我們看下圖,在WebServer和DB之間加一層cache,這層cache一般選取的介質是內存,因為我們都知道存入數據庫的數據都具有持久化的特點,那么讀寫會有磁盤IO的操作,內存的讀寫速度遠比磁盤快得多。(選用存儲介質,提高訪問速度:內存>>磁盤;減少磁盤IO的操作,減少重復查詢,提高吞吐量)
業務層緩存
除了上面介紹的緩存場景,我們還可能需要更細粒度的緩存,可以在上圖的某個切面做更細致處理,下篇我們將從實踐方面來探討業務上的緩存。
存儲介質訪問速度比較 來自Google工程師Jeff Dean的分享,僅供參考:
存儲介質 | 速度 |
---|---|
L1 cache reference 讀取CPU的一級緩存 | 0.5 ns |
Branch mispredict(轉移、分支預測) | 5 ns |
L2 cache reference 讀取CPU的二級緩存 | 7 ns |
Mutex lock/unlock 互斥鎖\解鎖 | 100 ns |
Main memory reference 讀取內存數據 | 100 ns |
Compress 1K bytes with Zippy 1k字節壓縮 | 10,000 ns |
Send 2K bytes over 1 Gbps network 在1Gbps的網絡上發送2k字節 | 20,000 ns |
Read 1 MB sequentially from memory 從內存順序讀取1MB | 250,000 ns |
Round trip within same datacenter 從一個數據中心往返一次,ping一下 | 500,000 ns |
Disk seek 磁盤搜索 | 10,000,000 ns |
Read 1 MB sequentially from network從網絡上順序讀取1兆的數據 | 10,000,000 ns |
Read 1 MB sequentially from disk 從磁盤里面讀出1MB | 30,000,000 ns |
Send packet CA->Netherlands->CA 一個包的一次遠程訪問 | 150,000,000 ns |
緩存的工作原理
一句話概況:更快讀寫的存儲介質+減少IO+減少CPU計算=性能優化。
通過上述應用場景(標紅部分),我們可知緩存的基本原理就是通過這幾個方面來進行優化的。
在實際應用中,我們需要對數據進行分類,才能更好的使用緩存以及一些策略來輔助。比如哪些為冷熱數據?哪些數據量很大,讀取會嚴重影響IO?哪些數據查多改少(日志數據,爬蟲數據)?哪些數據又是經過很復雜的計算得到的結果(這些珍貴的數據需要好好保存利用)?……
緩存帶來的好處
顯而易見,緩存給我們帶來最直接的體驗就是“快”,我們來總結一下:
-
通過減少IO(包括磁盤和網絡)來提高吞吐量,減少計算量(CPU計算)釋放CPU,這些都是提高系統的響應速度。
-
通過切面的處理方式,可以在各層進行插拔,是所有性能優化最簡單有效的解決方案。(對於不熟悉業務代碼或算法的優化者,顯然加一層緩存的復雜度和風險更低,而這一層看似簡單的緩存,它給系統帶來的性能優化有可能大大超過前者)
緩存帶來的困擾
我們不能否認緩存給我們帶來諸多便利,同時,我們不能忽略緩存確實也給我們帶來了不少困擾:
-
數據的一致性、實時性受影響。(需要對數據的一致性,時效性進行評估,進而確定是否要緩存或設定緩存的過期時間,比如個性化的數據是否值得緩存?)
-
緩存介質帶來的不可靠性。(一般使用內存做緩存的話,若機器故障,如何保證緩存的高可用?可考慮對緩存進行分布式做成高可用,同時,需要接受這種不可靠不安全會給數據帶來的問題,在異常情況下進行補償處理,定期持久化等方式)
-
緩存的數據使得更難排查問題。因為緩存命中是隨着訪問隨時變化的,緩存的行為難以重現,使得出現BUG很難排查。
-
進程內緩存可能會增加GC壓力:在具有垃圾收集功能的語言中(如java),大量長壽命的緩存對象會增加垃圾收集的時間和次數。
我前面提到的,使用緩存之前我們需要對數據進行分類,對訪問行為進行預估,思考哪些數據需要緩存,緩存時需要采用什么策略?這樣,我們才不被緩存所困擾,才能規避這些問題。
常用的緩存工具
業務上緩存,常用 and 開源的緩存工具有:ehcache、memcache、redis。
-
ehcache
是一個純Java的進程內緩存框架,hibernate使用其做二級緩存。同時,ehcache可以通過多播的方式實現集群。本人主要用於本地的緩存,數據庫上層的緩存。 -
memcache
是一套分布式的高速緩存系統,提供key-value這樣簡單的數據儲存,可充分利用CPU多核,無持久化功能。本人在做web集群的時候用過,主要做session共享,頁面對象緩存。 redis
高性能的key-value系統,提供豐富的數據類型,單核CPU有抗並發能力,有持久化和主從復制的功能。本人主要使用redis的redis sentinel,根據不同業務分為多組。