分佈式系統關注點(16)——360°全方位解讀「緩存」

如果這是第二次看到我的文章,歡迎右側掃碼訂閱我喲~  👉

本文長度爲3578字,建議閱讀10分鐘。

堅持原創,每一篇都是用心之作~

 

此前的「伸縮性」章節結束了,此文是「高性能」章節的第一篇。

 

只要是位正兒八經的程序員自然知道「緩存」是什麼,甚至我司的很多做運營的小姐姐現在和程序員小哥哥的交流中都時不時冒出「緩存」字眼,讓人壓力山大。(本文討論的「緩存」皆指的是軟件層面運用的緩存

 

大家都知道的一點是,緩存可以讓原本打開很慢的頁面,變得能“秒開”。你平時訪問的APP、網站幾乎都有涉及到緩存的運用。

 

那麼,緩存除了能加速數據的訪問之外,還有什麼作用呢?

 

另外,任何事物都有兩面性,我們如何才能將緩存的優點發揮得淋淋盡致,同時避免掉到它的弊端中呢?

 

Z哥今天想分享給你的就是我對緩存的理解和運用的思路,希望對你有所啓發。

 

 

「緩存」能做什麼?

正如前面所說,大家最普遍的理解就是當我們遇到某個頁面打開很慢的時候,會想到引入緩存,這樣頁面打開就快了。

 

其實快和慢都是相對的,從技術角度來說,緩存之所以快是因爲緩存是基於內存去建立的,而內存的讀寫速度比硬盤快X倍,所以用內存來代替硬盤作爲讀寫的介質自然能大大提高訪問數據的速度。

 

這個過程大致是這樣的,通過在內存中存儲訪被問過的數據供後續訪問時使用,以此來達到提速的效果

 

 

 

其實除此之外,緩存還有另外2個重要的運用方式,「預讀取」和「延遲寫」。

 

 

預讀取

預讀取就是預先讀取將要載入的數據,也可以稱作「緩存預熱」。就是在系統對外提供服務之前,先將硬盤中的一部分數據加載到內存中,然後再對外提供服務

 

 

爲什麼要這樣做呢?因爲有些系統一旦啓動就要面臨上千上萬的請求進來(在一些toC的項目尤其如此),如果直接讓這些請求打到數據庫上,非常大的可能是數據庫壓力暴增,直接被幹趴,無法正常響應。

 

爲了緩解這個問題,需要通過「預讀取」來解決。

 

可能你會問,哪怕用了緩存還是扛不住呢?那就是做橫向擴展+負載均衡的時候到了。(可以點擊文末鏈接閱讀之前的《彈性架構》系列

 

 

如果說「預讀取」是在「數據出口」加了一道前置的緩衝區的話,那麼顧名思義,下面要說的「延遲寫」就是在「數據入口」後面加了一道後置的緩衝區。

 

 

延遲寫

你可能知道,數據庫的寫入速度是慢於讀取速度的,因爲寫入的時候有一系列的保證數據準確性的機制。

 

所以,如果想提升寫入速度的話,要麼做分庫分表,要麼就是通過緩存來進行一道緩衝,再一次性批量寫到磁盤,以此來提速。

 

題外話:由於分庫分表對跨表操作以及多條件組合查詢的副作用巨大,所以引入它的複雜度遠大於引入緩存,我們應當優先考慮引入緩存的方案。

 

 

那麼,通過緩存機制來加速“寫”的過程就可以稱作「延遲寫」。就是預先將需要寫入到磁盤或者數據庫的數據,先暫時寫入到內存,然後就返回成功。再定時將內存中的數據批量寫入到磁盤

 

 

可能你會想,寫到內存就認爲成功,萬一中途出現意外、斷電、停機等導致程序異常終止的情況,數據不就丟了嗎? 

 

是的。所以,「延遲寫」一般僅用於對數據完整性要求不是那麼苛刻的場景。比如點贊數啊、參與用戶數啊等等,可以大大緩解對數據庫頻繁修改所帶來的壓力。

 

其實在我們熟知的分佈式緩存Redis中,其默認運用的持久化機制——RDB,也是這樣的思路。

 

 

在一個成熟的系統中,能夠運用到緩存的地方其實並不是一處。下面Z哥就來幫你梳理一下我們在哪些地方可以“加緩存”。

 

 

哪裏可以加「緩存」?

在說哪裏可以加緩存之前我們先搞清楚一個事情,我們要緩存什麼?也就是符合什麼特點的數據才需要加緩存?畢竟加緩存是一個額外的成本投入,得物有所值。

 

一般來說你可以用這兩個標準來判斷:熱點被高頻訪問,如幾十次/秒以上)數據、靜態(很少變化,讀遠大於寫,如幾天變更一次)數據

 

接下去就可以替它們找到合適的地方加緩存了。

 

 

緩存的本質是一個“防禦性”的機制,而系統之間的數據流轉是一個有序的過程。所以,選擇在哪裏加緩存就相當於選擇在一條馬路的哪個位置設路障。在這個路障之後的道路都能受到保護,不被車流碾壓。

 

那麼在以終端用戶爲起點,系統所用的數據庫爲終點的這條道路上可以作爲緩存設立點的位置大致有以下這些。

 

 

每個設立點可以擋掉一些流量,最終形成一個漏斗狀的攔截效果,以此保護最後面的系統以及最終的數據庫。

 

 

下面Z哥來簡要描述下每一個的運用場景以及需要注意的點。

 

 

瀏覽器緩存

這是離用戶最近的可以作爲緩存的地方,而且藉助的是用戶的“資源”(緩存的數據在用戶的終端設備上),性價比可謂最好,讓用戶幫你分擔壓力

 

 

當你打開瀏覽器的開發者工具,看到from cache或者from memory cache、from disk cache的時候,就意味着這些數據已經被緩存在了用戶的終端設備上了(沒網的時候也能訪問到一部分內容就是這個原因)。

 

這個過程是瀏覽器替我們完成的,一般用於緩存圖片、js、css這些。我們可以通過Http消息頭中的Cache-Control來控制它,具體細節這裏就不展開了。

 

js裏的全局變量、以及cookie等運用也屬於該範疇。

 

瀏覽器緩存是在於用戶側的緩存點,所以我們對其的掌控力就差很多,在沒有發起新請求的情況下,你無法主動去更新數據。

 

 

CDN緩存

提供CDN服務的服務商,在全國甚至是全球部署着大量的服務器節點(可以叫做「邊緣服務器」)。

 

那麼將數據分發到這些遍佈各地服務器上作爲緩存,讓用戶訪問就近的服務器上的緩存數據,就可以起到壓力分攤和加速效果。這在ToC類型的系統上運用,效果格外顯著。

 

但是需要注意的是,由於節點衆多,更新緩存數據比較緩慢,一般至少是分鐘級別。所以一般僅適用於不經常變動的靜態數據。

 

題外話:解決方式也是有的,就是在url後面帶個自增數或者唯一標示,如?v=1001。因爲不同的url會被視作“新”的數據和文件,被重新create出來。



網關(代理)緩存

到這裏做緩存就是在你自己的地盤了。很多時候我們會在源站前面架一層網關(或者說反向代理、正向代理),爲的是做一些安全機制或者統一分流策略的入口。

 

 

同時這裏也是做緩存的一個好場所。畢竟網關是“業務無關性”的,它能夠攔下來的請求,對背後的源站也是很大的受益,減少了大量的CPU運算。

 

常用的網關(代理)緩存有Varnish,Squid,Ngnix。一般情況下,簡單的緩存運用場景,用nginx即可,因爲大部分時候我們會用它來做負載均衡,能少引入一個技術就少一份複雜度嘛。如果是大量的小文件可以使用Varnish,而Squid則相對大而全,運用成本也更高一些。

 

 

進程內緩存

一個請求能走到這裏說明他是“業務相關”的,需要經過業務邏輯的運算。

 

也正因爲如此,從這裏開始對緩存的引入成本比前面3種大大增加,因爲對緩存與數據庫之間的「數據一致性」要求更高了。

 

 

可能我們大多數程序員第一次刻意使用緩存的場景就是這個時候。進程內和進程外的緩存運用中有很多的細節需要注意,這些也是我們後續文章的內容,所以我們後續再詳聊。

 

 

進程外緩存

這個大家也熟悉,就是redis、memcached之類,甚至也可以自己單獨寫一個程序來專門存放緩存數據,供其他程序遠程調用。

 

同樣,這裏的細節我們後續再聊,這裏先多說幾句關於redis和memcached該怎麼選擇的建議。

 

對資源(cpu、內存等)利用率格外重視的話可以使用Memcached,但程序在使用的時候需要容忍可能發生的數據丟失,因爲是純內存的機制。如果無法容忍這點,並且對資源利用率也比較豪放的話可以使用redis。而且redis的數據庫結構更多,Memcached只有key value,更像是一個nosql存儲。

 

 

數據庫緩存

數據庫本身自帶緩存模塊的,否則也不會叫它內存殺手,基本上你給多少內存就能喫多少。

 

數據庫緩存是數據庫的內部機制,我們這裏就不深入下去了。一般都會給出設置緩存空間大小的配置來讓你進行干預。

 

 

最後,其實磁盤本身也有緩存。所以你會發現,爲了讓數據能夠平穩的寫到物理磁盤中真的是一波三折,不知道什麼時候可以有“快”到不需要程序來考慮緩存的磁盤出現來拯救我們程序員呢

 

 

「緩存」是Silver bullet嗎?

可能你會想緩存那麼好,那麼應該多多益善,只要慢就上緩存來解決?

 

一個事物看上去再好,也有它負面的一面。緩存也有一系列的副作用需要考慮。除了上面提到的「緩存更新」和「緩存與數據的一致性」問題,還有諸如:

  1. 緩存雪崩

  2. 緩存穿透

  3. 緩存併發

  4. 緩存無底洞

  5. 緩存淘汰

  6. ...

等等問題,這些Z哥會在接下去的文章中和你一起深入剖析。

 

想第一時間瞭解這些的可以「關注」Z哥一波~

 

 

總結

好了,我們總結一下。

 

這次呢,Z哥先向你介紹了運用緩存的三種思路。

 

然後梳理了在一個完整的系統中可以設立緩存的幾個位置,並且分享了關於瀏覽器緩存、CDN緩存、網關(代理)緩存的一些使用經驗。

 

希望對你有所啓發。

 

 

後續的文章我將着重深入「進程內緩存」和「進程外緩存」的最佳實踐,等我再次出現

 

 

 


 

相關文章:

 


 

作者:Zachary

出處:https://zacharyfan.com/archives/646.html

 

如果你喜歡這篇文章,可以點一下右下角的「推薦」。

 

這樣可以給我一點反饋。: )

 

謝謝你的舉手之勞。

 

▶關於作者:張帆(Zachary,個人微信號:Zachary-ZF)。堅持用心打磨每一篇高質量原創。歡迎掃描右側的二維碼~。

定期發表原創內容:架構設計丨分佈式系統丨產品丨運營丨一些思考。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章