快取有那麼多種,分別是幹什麼的?

2022-07-05 10:00:14 字數 4193 閱讀 8222

只要是位正兒八經的程式設計師應該都知道「快取」是什麼,甚至我司的很多做運營的小姐姐現在和程式設計師小哥哥交流中都時不時冒出「快取」這個詞,讓人壓力山大。

那麼,快取除了能加速資料的訪問之外,還有什麼作用呢?

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

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

其實快和慢都是相對的,從技術角度來說,快取之所以快是因為快取是基於記憶體去建立的,而記憶體的讀寫速度比硬碟快 x 倍,所以用記憶體來代替硬碟作為讀寫的介質自然能大大提高訪問資料的速度。

這個過程大致是這樣的,通過在記憶體中儲存被訪問過的資料供後續訪問時使用,以此來達到提速的效果。

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

預讀取就是預先讀取將要載入的資料,也可以稱作「快取預熱」,它是在系統中先將硬碟中的一部分資料載入到記憶體中,然後再對外提供服務。

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

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

可能你會問,哪怕用了快取還是扛不住呢?那就是做橫向擴充套件+負載均衡的時候到了,這不是本文討論的內容,有機會再專門分享吧。

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

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

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

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

那麼,通過快取機制來加速「寫」的過程就可以稱作「延遲寫」,它是預先將需要寫入到磁碟或者資料庫的資料,暫時寫入到記憶體,然後就返回成功,再定時將記憶體中的資料批量寫入到磁碟。

可能你會想,寫到記憶體就認為成功,萬一中途出現意外、斷電、停機等導致程式異常終止的情況,資料不就丟了嗎? 

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

其實在我們熟知的分布式快取 redis 中,其預設運用的持久化機制——rdb,也是這樣的思路。

在乙個成熟的系統中,能夠運用到快取的地方其實並不是一處。下面就來梳理一下我們在哪些地方可以「加快取」。

在說**可以加快取之前我們先搞清楚乙個事情,我們要快取什麼?也就是符合什麼特點的資料才需要加快取?畢竟加快取是乙個額外的成本投入,得物有所值。

一般來說你可以用這兩個標準來判斷:

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

快取的本質是乙個「防禦性」的機制,而系統之間的資料流轉是乙個有序的過程,所以,選擇在**加快取就相當於選擇在一條馬路的哪個位置設路障。在這個路障之後的道路都能受到保護,不被車流碾壓。

那麼在以終端使用者為起點,系統所用的資料庫為終點的這條道路上可以作為快取設立點的位置大致有以下這些:

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

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

這是離使用者最近的可以作為快取的地方,而且借助的是使用者的「資源」(快取的資料在使用者的終端裝置上),價效比可謂最好,讓使用者幫你分擔壓力。

當你開啟瀏覽器的開發者工具,看到 from cache 或者 from memory cache、from disk cache 的時候,就意味著這些資料已經被快取在了使用者的終端裝置上了,沒網的時候也能訪問到一部分內容就是這個原因。

這個過程是瀏覽器替我們完成的,一般用於快取、js 與 css 這些資源,我們可以通過 http 訊息頭中的 cache-control 來控制它,具體細節這裡就不展開了。此外,js 裡的全域性變數、cookie 等運用也屬於該範疇。

瀏覽器快取是在於使用者側的快取點,所以我們對它的掌控力會比較差,在沒有發起新請求的情況下,你無法主動去更新資料。

提供 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 儲存。

資料庫本身是自帶快取模組的,否則也不會叫它記憶體殺手,基本上你給多少記憶體就能吃多少。資料庫快取是資料庫的內部機制,一般都會給出設定快取空間大小的配置來讓你進行干預。

最後,其實磁碟本身也有快取。所以你會發現,為了讓資料能夠平穩地寫到物理磁碟中真的是一波三折,不知道什麼時候可以有「快」到不需要程式來考慮快取的磁碟出現來拯救我們程式設計師呢。

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

乙個事物看上去再好,也有它負面的一面,快取也有一系列的***需要考慮。除了前面提到的「快取更新」和「快取與資料的一致性」問題,還有諸如下邊的這些問題:

所以快取不是銀彈,對快取的使用也需要先考慮各種問題。總結一下,本文先向你介紹了運用快取的三種思路,然後梳理了在乙個完整的系統中可以設立快取的幾個位置,並且分享了關於瀏覽器、cdn 與閘道器(**)等快取的一些使用經驗,沒有具體展開來講細節,只是希望你對快取有乙個更加體系化的認識,希望能讓你看得更加全面。

注出處:

銀行需要發行那麼多種卡嗎?

今天去招行,發現推廣頁上印了很多種信用卡,共有12類,好幾十種 這些卡,大多為企業聯名卡與個性卡 hellokitty卡,nba巨星卡 msn mini卡 真正體現出功能差異 許可權範圍 的也就那麼幾種 在我想來,信用卡是珍貴而私密的東西,並不適合拿出來展示炫耀 不同於汽車的品牌 所以那些個性卡恐怕...

銀行需要發行那麼多種卡嗎?

今天去招行,發現推廣頁上印了很多種信用卡,共有12類,這些卡,大多為企業聯名卡與個性卡 hellokitty卡,nba巨星卡 msn mini卡 真正體現出功能差異 許可權範圍 的也就那麼幾種 在我想來,信用卡是珍貴而私密的東西,並不適合拿出來展示炫耀 不同於汽車的品牌 所以那些個性卡恐怕不能增加多...

銀行需要發行那麼多種卡嗎?

今天去招行,發現推廣頁上印了很多種信用卡,共有12類,這些卡,大多為企業聯名卡與個性卡 hellokitty卡,nba巨星卡 msn mini卡 真正體現出功能差異 許可權範圍 的也就那麼幾種 在我想來,信用卡是珍貴而私密的東西,並不適合拿出來展示炫耀 不同於汽車的品牌 所以那些個性卡恐怕不能增加多...