分散式系統關注點——360°全方位解讀「快取」

huorongbj發表於2019-03-21

如果這是第二次看到我的文章, 歡迎 訂閱我的公眾號(跨界架構師)喲~ 

本文長度為 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://www.cnblogs.com/Zachary-Fan/p/smalltarget.html

 

如果你喜歡這篇文章,可以點一下左下角的「    大拇指    」。

 

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

 

謝謝你的舉手之勞。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/31544142/viewspace-2638601/,如需轉載,請註明出處,否則將追究法律責任。

相關文章