讓網站速度飛快的秘密,你了解什麼是網頁快取(Cache)嗎?

讓網站速度飛快的秘密,你了解什麼是網頁快取(Cache)嗎?

躲在銀幕後面的強大幫手,用一個便當的時間了解快取機制

作者: Vincent Ke 更新日期: 2020/10/03

相信逛過電商網站過的你們,一定有一個經驗,在網站進行特賣時,總是害怕因為流量過大造成網站的堵塞,如果在使用者過多的情況下,的確是有可能造成網站上效能的遲緩。因為每當你到首頁一次,就要去資料庫抓取一次首頁全部的資料。想想一個大型的購物網站,光在首頁可能就有百項商品,千百張大大小小的圖,這樣的情況在對資料庫撈取上完全沒有設計的情況下,會是多麼大的負擔?

但是我們都知道,其實網站上有部分的資訊,在短時間內不會輕易有太多的波動,而這些不常改變的資料(通常是圖片),如果每登入一次就要請求一次,那資源也太浪費了吧,如果先把這些”已經”查看過的部分,保留在某一個角落,等到請求時若是發現已經看過後,就可以用極快的速度撈出來,而不是再到資料庫裡面重新算一次,這樣的方式就是我們說的Cache(快取,或緩存)。

校正小編補充:
要讓網站變快並不是只有一種方法,而且每種方法彼此之前是可以共存的。本篇主要是以降低資料庫使用率為出發。
不過資料庫在網站使用上的問題跟執行緒和資料同步有關。最精簡的講法是,只要資料有同步問題,就無法使用執行緒的優勢,不能同時開兩個程式去修改同一個資料,他們必須要”排隊“依序修改。只要有排隊機制,就等於要等,要等就會花時間。網站裡面的花時間就是網頁讀取變慢。所以減少資料庫的撈取與資料變更是可以有效的讓網站速度變快的其中一種方法。

首先簡單介紹Cache的歷史吧,Cache這名詞來自1967年的一篇電子工程期刊論文,在早期PC-AT/XT和80286的時中,當時並沒有Cache的存在,CPU和記憶體因為沒有相因應的作用方式,CPU都是透過直接存取記憶體來作業,造成效能和速度緩慢。而在80386的晶片組開始,多了Cache的概念做設計,當CPU進行資料處理時,不再透過直接讀取記憶體的方式,而是會先到”Cache”中去尋找要處理的資料,如果這筆要處理的資料,因之前的操作已經從記憶體被讀取後,就會被暫存在Cache中。

這樣的目的主要是為了讓讓資料存取的速度適應CPU的處理速度,透過提取「暫存不久前存取過的資料」的概念,來實現使CPU不再透過讀取記憶體造成效能降低,如今快取的概念的應用上已經不僅在CPU和主記憶體之間有,而且在記憶體與硬碟之間,也可以實現Cache(磁碟快取)。

所以Cache是一種概念,我們可以想像Cache是一個資料夾,會把你之前處理資料的結果,做成類似暫存檔的方式紀錄在那,並有一個做索引查詢的列表檔案,去紀錄每一支暫存檔,每一個暫存檔都有一個對應的唯一值,等到下次需要查找或處理同樣的檔案時,我們會先去Cache(資料夾)裡頭找到列表檔案內,是不是有上次紀錄的暫存處理結果,如果有的話就先把暫存檔呼叫出來繼續處理,而非每一次都需要去記憶體或是硬碟的方式做查找,來提升效能。

校正小編補充:
早期很多機器並不像現在記憶體分成很多種,有RAM、有硬碟、有ROM、有SD卡等等,這些全部都可以被稱之為”記憶體“。CPU在讀取資料的時候其實也不知道他對應的是誰,只知道有讀取到資料、對方有回應。但是現在的CPU都超快,所以很可能因為太快了所以會漏掉資料,有點像是CPU點了單,服務生還沒開口講話,就惱羞覺得等太久就走了。因此必須要有個中間人員(裝置),配合CPU的速度,叫他等等,再轉過身跟服務生傳話做動作。

這個裝置叫做記憶體控制器(Memory controller) 或是被包含在早期的北橋晶片(North Bridge chip) 。不過科技是一直在進步的,所以如果是更早期的晶片,其實是連記憶體控制器都沒有的。總之無論是什麼情況,記憶體讀寫速度大致上都比不上CPU的速度,然後記憶體讀寫速度越快,價錢越貴。所以他們就想出了一個方法,讓還沒處理的檔案,放在比較慢比較便宜的記憶體上,處理中的就運行在比較快的記憶體上,這就是我們平常的RAM與硬碟的使用方式。

當要執行程式的時候,先把資料讀到RAM上。在讓CPU與RAM溝通。所以如果RAM不夠的情況下要多執行程式,其實我們是可以運行在硬碟上的。這是虛擬記憶體的其中一種使用方式。但可想而知速度會被拖累很多,這也是為什麼RAM越多電腦跑得越順的原因,因為可以同時處理更多的程式(包含電腦系統)。

不過如果今天在記憶體上所做的事情很大部分都是一樣的事,那我們還要一直處理嗎?
例如
一顆蘋果10元,一根香蕉價錢等於半個蘋果,一個橘子價錢等於2根香蕉。
如果有一本書價值10個橘子,請問是價值多少錢。

一般來說,我們會先把橘子算成錢,也就是1個橘子10元 (10 * 0.5 * 2)。
然後再去乘與10倍。所以是100元。

倘若今天題目改成我想要知道一本價值10個橘子的書與一顆價值5個橘子的球,個別是多少錢。
人類的算法會是先算出上面的100。
再拿5 * 10 = 50,就知道球的價值。

但是電腦不是這樣,因為當算完書以後,電腦因為記憶體寶貴,所以會忘掉原本橘子的價值。
因此會再重算一遍橘子,在算出球。
所以電腦可能會是4個步驟。
1. 算出橘子
2. 算出書
3. 算出橘子
4. 算出球

如果今天又多出其他物品是以橘子計價並且要改成現金計價,那每個金額計算都會重複算出橘子的價錢。

這時你可能會想說,怎麼那麼笨,先把橘子的金額儲存起來就好了啊。

沒錯,這就講對了,而且這就是快取概念的雛形。
當你把橘子的金額儲存起來放到特定的地方,並且每次都去這特定的儲存地點取出這個金額。
這整件事就可以稱之為快取Cache機制,並且當你成功在這特定的儲存地點拿到橘子的金額,就稱作Hit(快取命中)。

而且你會發現這個概念中其實沒有說儲存裝置一定要很快,只是從前面的邏輯來看,如果這個儲存裝置也就是記憶體越快,電腦會跑得越順暢。

所以在早期的時候,快取是容量很小很快的記憶體裝置。並且有個控制器在管理他,要不然他如果存書或是存球的金額,就也只是在浪費空間,沒有被重複利用。這個控制器有可能被放在CPU裡面做套裝,或是一般常見的放在Memory controller旁邊就近管理。而且Cache只是個概念,所以理論上都放也無所謂。想要的話硬碟裝置也可以放一個Cache,網路控制器也可以放,主要是看成本以及效益夠不夠(怕貴也可以用SD卡做快取,只是可能不會變快而已)。

所以回到網路端的快取,能使用的多半只有RAM跟硬碟。而以特性來說,如果快取對象的資料量很大,我們就會放在硬碟裡,也就是會變成實體的檔案。如果資料量比較小,就會放在RAM裡面,畢竟比較快。像MySQL等資料庫,其實多半都有內建類似的機制,只是不叫Cache,比較常見的是名稱是“View”。

不過,快取機制最大的問題就是無法及時刷新,比方上面的例子中,如果算到一半蘋果漲價了,那漲價的時間點之後所有金額都會是錯的。所以如何刷新Cahce資料,什麼時候刷新通常都是Cache與類似Cache機制的最大問題。

當然快取的地位一開始只是用來最佳化系統,但逐漸已經演化成許多大型網站不可或缺的必要功能,包括Facebook、Twitter或IG等,都在資料庫中靈活的應用了快取技術。 因為上述大多數的網路服務,都是透過前端的應用程式伺服器與資料庫去做組合而成,當資訊海量的時候,每一秒鐘的上百萬次的查找都有可能是讓系統效能延遲的殺手,所以才利用快取(Cache)來儲存經常存取的資料以強化資料庫並改善服務效能,但Client端及Server端其實都有實現Cache的做法。

校正小編補充
在各個不同的層級實現Cache會需要寫不同的程式,所以程式上可以把它想成不同的事情。利用RAM做作業系統的快取與利用硬碟資料夾空間實踐HTML頁面快取。一個寫個可能是C語言,另外一個可能是PHP、Ruby on rails 或是其他的語言。當然想法可能接近,但是函式庫與語法應該會是完全不一樣。當然,光是在網站運用上面,Client端及Server端的實現也是完全的不一樣。

先從Client端講起,例如像是租房網或是租車網的商品圖。如果首頁有上百張的商品圖,那每一次進入就下載個幾次,等到使用者大量湧入時,這樣的流量對系統就會是一個衝擊,那不如透過Cache的機制把圖片”暫存”到使用者端的瀏覽器(通常也是記憶體上做Cache)吧,這樣只有第一次瀏覽網站的時候需要下載,之後只要在Cache過期前進入網站,都可以透過Cache機制來避免讀取過慢。這樣的目的就是為了「降低使用者端的 Request 發送」與「減少Server Response回去使用者端資料」造成的遲緩和浪費。

但網站總有更新的時候吧,總不能一直讓用舊圖吧,因此在HTTP 1.1中就設計了以下兩種機制來達成目的,讓Cache是在 Client 與 Server 之間透過 Header 的「交互作用」機制來完成。所以Cache會用Expire Date和Cache-control的交互應用來處理Cache的效期,例如早上九點會發出request去取得Cache的資料做更新,就表示在Expire Date之前瀏覽器已經認為這些資料都是新的,並且不會發送Request;但Expire Date 只能指定固定日期,所以可以結合HTTP Response中的Header,裡有一個“Cache-Control: max-age = 100(秒)”,表示你請求的資源在cache中的最久可活100秒,過一百秒後就會再次請求了。

但當然除了設計Cache的期限之外,也可以讓自己的瀏覽器端不使用Cache的no-Cache機制;也有因為請求資料的公開性來做的一些設計(A可不可以用B的cache),那這部分都可以在Cache-Control 做調整囉,就先不多贅述了。

而Server端的Cache,目的就是為了避免使用者對資料庫的大量寫入並獲得結果(Input/output DB資料庫)造成效能上的耗竭,而Server端使用Cache比較常見的,就是Memcache已及Redis兩種。

Memcached其實就是Memory cache 的意思,Memcache 是一套 Name-Value Pair(NVP) 分散式記憶體快取系統,主要是C語言的架構,概念上就是透過 Memory Cached 使用Key/Value的方式,如果資料已經暫存在Server端的 memory Cached 中,就直接就可以回覆使用者該資訊。若沒有就會把資料寫入/讀取資料庫,才把資料寫一份快取並進到快取記憶體內。

而Memcached並非是做在本身的Web server或是DB server等主機上,而是透過使用多台的Server主機做為 Memcached Server的機制,並將這幾台主機變成群組,好處除了可以讓未來的延展性變得更高,因為若是快取記憶體不足只要增加快取記憶體伺服器即可,而這樣也可以用來解決共享記憶體只能單機應用的侷限。而這樣的另外一個好處就是可以把每一個應Web server的Cache資料分散到每一個Memcached主機,來達到資料分散的做法(分散式快取 )。而這樣的好處是對於不同主機上的快取記憶體資料,可以採用預先分配的方式,來有效降低記憶體因過多Cache造成太碎片化的問題,但也會因為使用量過大,往往造成帶來浪費多台Memcache群組裡記憶體空間的情況。

編輯小編補充:
簡單來說就是把資料放在RAM裡面做Cache,但是一台主機的RAM是有限的,資料組合的排列組合是將近無限的。
所以用多台主機的RAM合再一起做Cache來解決這個問題,這就是所謂的分散式記憶體快取系統。如果你的網站內容組合很少。單用一台主機RAM就可以達成,那也不一定要”分散式“。

而Redis 也是另外一個常見的Cache方式 ,Redis在Open Source裡面,屬於非常熱門的NOSQL資料庫,僅管也是利用Server端的記憶體做Cache,但因為本身是一個 in-memory 的 key-value database, 而NoSQL的Redis 通常只使用處理器的單一執行緒,來有效降低記憶體碎片化到其它核心上的情況。而平均在每一個核上的Redis在處理小數據時,性能會比Memcached來得更高。且Redis提供了一個資料庫架構,可以自動排序被儲存在Redis中的資料,讓開發者取得排序後的資料,也可以使用多節點的方式來達到分散式的做法。

但Memcached也有一些好處,例如Memcached可以支援多個處理器核心和執行緖,與Redis比起,更可充分利用現代處理器的效能,並且採用預先分配的記憶體裡的零碎空間,而使得實際的記憶體使用量較Redis要少,減少記憶體分配壓力,但就像上述提到的,過多過散的齡歲空間也會壓縮到整體記憶體的空間

但畢竟沒有對錯,只有適合與不適合之間的差異落差,但記得不論是Memcached還是Redis ,Cache只是一種應用上的概念,適合度本身是要回歸你產品本身的架構性來做思考,而Memcache 以及Redis本身也很適合用在實現Session的機制上(例如存放SessionID來呼叫Session裡的資料),所以哪個好用就是見仁見智囉!

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *