大家好, 我是「老黑」。
緩存,已經(jīng)是一個老生常談的技術了,在高并發(fā)讀的情況下對于讀服務來說可謂是抗流量的銀彈。
高并發(fā)三大利器:緩存、限流、降級。
今天我們就來談談緩存。「對于緩存,我的理解是讓數(shù)據(jù)更接近于用戶,目的是讓用戶的訪問速度更快。」 所以距離越接近用戶的緩存,越快越有效!緩存的工作原理是先從緩存中獲取數(shù)據(jù),如果有數(shù)據(jù)則直接返回給用戶,如果沒有數(shù)據(jù)則從慢速設備上讀取實際數(shù)據(jù)并且將數(shù)據(jù)放入緩存。
按照層級關系,我們來劃分一下緩存,同時也是我們今天的「大綱」:
瀏覽器緩存
瀏覽器是我們網(wǎng)上沖浪的重要工具,為了能夠讓我們順暢的沖浪,它也會幫助我們緩存一些東西,主要存放一些實時性不太敏感的數(shù)據(jù),比如商品詳情頁框架、商家評分、評價、廣告詞等。對于實時性要求高的數(shù)據(jù)則不能使用瀏覽器緩存。瀏覽器緩存是有過期時間的,我們可以通過對響應頭Expires、Cache-control進行控制。
客戶端緩存
客戶端緩存很容易理解,意思就是存放在客戶端的緩存。它的使用場景不多,在我們大促的時候,為了防止瞬間流量把服務端擊垮,一般會在大促來臨之前把app需要訪問的一些素材(如js/css/image等)提前下發(fā)到客戶端進行緩存,在大促來臨之際app就不需要去拉取這些素材了。另外的話還有一些兜底數(shù)據(jù)或者樣式文件也會存放于客戶端緩存中,在服務端異常或者網(wǎng)絡異常的時候保證app不崩。
CDN緩存
CDN(Content Delivery Network),即內(nèi)容分發(fā)網(wǎng)絡。它是建立并覆蓋在承載網(wǎng)之上,由分布在不同區(qū)域的邊緣節(jié)點服務器群組成的分布式網(wǎng)絡。我們通常會將一些靜態(tài)頁面數(shù)據(jù)、活動頁面、圖片等數(shù)據(jù)存放于CDN緩存中。
CDN緩存有兩種機制:推送機制(當內(nèi)容變更后主動將數(shù)據(jù)推送到CDN節(jié)點)和拉取機制(先訪問CDN節(jié)點,無數(shù)據(jù)的時候會從源服務器獲取數(shù)據(jù)返回并存儲CDN節(jié)點)。
舉個例子,如果你要去買汽車,你應該是到4s店去買汽車,如果4s店有你可以直接提走,如果4s店沒有,那么4s店鋪需要去進一批貨,然后回到店鋪,然后再給你。在這個case中,4s店其實就承當了一個CDN緩存節(jié)點的角色。
反向代理緩存
反向代理,我們一般情況都是指反向代理服務器Nginx。
Nginx緩存主要分為Nginx Http緩存與Nginx代理層緩存。
Nginx Http緩存提供expires、etag、if-modified-since指令來實現(xiàn)反向代理緩存。Nginx代理層緩存主要以Http模塊與proxy_cacahe模塊進行配置即可。
本地緩存
本地緩存,一般是指將客戶機本地的物理內(nèi)存劃分出一部分空間用來緩沖客戶機回寫到服務器的數(shù)據(jù)。從全局的角度,我們可以有「磁盤緩存」、「CPU緩存」、「應用緩存」。
「磁盤緩存」分為讀緩存和寫緩存。
讀緩存是指,操作系統(tǒng)為已讀取的文件數(shù)據(jù),在內(nèi)存較空閑的情況下留在內(nèi)存空間中(這個內(nèi)存空間被稱之為“內(nèi)存池”),當下次軟件或用戶再次讀取同一文件時就不必重新從磁盤上讀取,從而提高速度。
寫緩存實際上就是將要寫入磁盤的數(shù)據(jù)先保存于系統(tǒng)為寫緩存分配的內(nèi)存空間中,當保存到內(nèi)存池中的數(shù)據(jù)達到一個程度時,便將數(shù)據(jù)保存到硬盤中。
「CPU緩存」可以分為一級緩存(L1 Cache)、二級三級緩存(L2/L3)。當CPU要讀取一個數(shù)據(jù)時,首先從L1中查找,沒有的話再從L2/L3中查找,如果還沒有那就從內(nèi)存中查找,內(nèi)存如果還沒有那就從磁盤查找。查找順序為:CPU->L1->L2/L3->內(nèi)存->磁盤。
「應用緩存」分為本地應用緩存與其他應用緩存。
本地應用緩存指的是本服務所使用的緩存,用Java服務來舉例,又分為 堆內(nèi)緩存 與 堆外緩存 。
堆內(nèi)緩存,一般指的是Java堆的緩存對象,堆內(nèi)緩存的好處是不需要序列化/反序列化,也是最快的緩存,缺點也很明顯,緩存數(shù)據(jù)多的時候,GC(垃圾回收)的頻率會增大,時間會加長。堆內(nèi)緩存一般使用軟引用/弱引用來引用對象,使用這兩種引用的好處是當堆內(nèi)存不足時,可以強制回收這部分內(nèi)存,釋放堆空間。堆內(nèi)緩存最大的問題是重啟時內(nèi)存中的緩存數(shù)據(jù)會丟失,如果堆內(nèi)緩存使用的多,再加上剛好流量風暴,有可能擊垮應用。堆內(nèi)緩存的實現(xiàn)一般有:Guava Cache、Ehcache等。
堆外緩存,這個聽說的同學比較少,它處于Java堆之外的內(nèi)存,不受GC控制,也不受限堆大小,只受限于機器內(nèi)存,所以,使用它一定小心謹慎,如果處理不當它可能存在內(nèi)存泄漏的風險!堆外內(nèi)存需要序列化/反序列化,所以它會比堆內(nèi)緩存慢一些。
其他應用緩存,指的是除了本服務之外的緩存,比如local redis cache。local redis cache指的是在本服務器上部署一組Redis,應用直接讀本機獲取緩存數(shù)據(jù),多機之間利用主從機制同步數(shù)據(jù)。這種方式的優(yōu)點是沒有網(wǎng)絡消耗,性能是最優(yōu)的。
分布式緩存
如果數(shù)據(jù)量不大的情況下,使用local redis cache的架構是最優(yōu)的。
使用local redis cache最大的問題是:
- 單機器容量問題
- 多實例數(shù)據(jù)一致性問題
- 多實例緩存命中率降低導致回源DB
如果遇到這樣的問題,那么應該將數(shù)據(jù)分片,盡可能的均勻分布到多臺服務器,這便是分布式緩存。
分布式緩存常見的分片策略有:
- 節(jié)點取余
- 一致性哈希
- 虛擬槽分區(qū)
我們最常見的Redis-Cluster集群則是使用虛擬槽分區(qū)的方式來對數(shù)據(jù)分片的。
我們點到即止,對于Redis緩存相關,后面會有很多文章來專門討論,敬請期待吧!
其他:緩存命中率
緩存命中率是我們非常重要的一個指標,我們?nèi)绻褂镁彺妫欢ㄐ枰ㄟ^監(jiān)控這個指標來看緩存的工作狀態(tài)。
它的計算方式為:
命中率緩存命中次數(shù)讀取總次數(shù)緩存命中率越高越好,如何提高緩存命中率呢?我們應該對于不同場景數(shù)據(jù)有不同的緩存策略,比如:
- 大促來臨之際應該提前將熱點數(shù)據(jù)緩存,這種方式我們稱之為緩存預熱或緩存熱加載;
- 在case1的基礎上,將熱點緩存數(shù)據(jù)與普通緩存數(shù)據(jù)做數(shù)據(jù)隔離,這一點前期需要人為干預,后期需要實時熱點發(fā)現(xiàn);
- 將數(shù)據(jù)分類,不同類別的數(shù)據(jù)配置合適的失效時間;
- 調(diào)整緩存粒度,通常情況下緩存粒度越小緩存命中率越高;
- 增大存儲容量,當容量不夠的時候會觸發(fā)過期策略導致部分緩存數(shù)據(jù)失效,從而影響緩存命中率;
緩存問題:緩存擊穿
[一句話概述]緩存擊穿是指數(shù)據(jù)庫和緩存都沒有的數(shù)據(jù),每次都要經(jīng)過緩存去訪問數(shù)據(jù)庫,大量的請求有可能導致DB宕機。(強調(diào)都沒有數(shù)據(jù)+并發(fā)訪問)
這里我繼續(xù)點到即止,后續(xù)奉上,敬請期待。
緩存問題:緩存穿透
[一句話概述]緩存擊穿是指數(shù)據(jù)庫有,緩存沒有的數(shù)據(jù),大量請求訪問這個緩存不存在的數(shù)據(jù),最后請求打到DB可能導致DB宕機。(強調(diào)單個Key過期+并發(fā)訪問)
這里我繼續(xù)點到即止,后續(xù)奉上,敬請期待。
緩存問題:緩存雪崩
[一句話概述]緩存擊穿是指數(shù)據(jù)庫有,緩存沒有的數(shù)據(jù),大量請求訪問這些緩存不存在的數(shù)據(jù),最后請求打到DB可能導致DB宕機。(強調(diào)批量Key過期+并發(fā)訪問)
這里我繼續(xù)點到即止,后續(xù)奉上,敬請期待。
緩存問題:緩存一致性
[一句話概述]緩存一致性指的是緩存與DB之間的數(shù)據(jù)一致性,我們需要通過各種手段來防止緩存與DB不一致,我們要保證緩存與DB的數(shù)據(jù)一致或者數(shù)據(jù)最終一致。
這里我繼續(xù)點到即止,后續(xù)奉上,敬請期待。
緩存的其他問題
緩存的好處我們非常受益,用戶的每一次請求都伴隨著無數(shù)緩存的誕生,但是緩存同時也給我們帶來了不小的挑戰(zhàn),比如在上面提到的一些疑難課題:緩存穿透、緩存擊穿、緩存雪崩和緩存一致性。
除此之外,我們還會涉及到其他的一些緩存難題,如:緩存傾斜、緩存阻塞、緩存慢查詢、緩存主從一致性問題、緩存高可用、緩存故障發(fā)現(xiàn)與故障恢復、集群擴容收縮、大Key熱Key......
我們今天只做一個緩存的開篇,具體的細節(jié),留給我們后續(xù)的章節(jié)中吧。
原文地址:https://mp.weixin.qq.com/s/XfEjjrinNaIIuETPrCLiuA