緩存設(shè)計:做好緩存設(shè)計的關(guān)鍵是什么?
緩存乃是一個用于臨時存儲數(shù)據(jù)的所在。當用戶進行數(shù)據(jù)查詢時,第一步便是在緩存中探尋,倘若找到了,便直接加以運用;要是未找到,就需前往數(shù)據(jù)的初始位置尋覓。因而,緩存實質(zhì)上屬于一種以空間換取時間的技術(shù)手段,借由數(shù)據(jù)在空間層面的重復(fù),來加快數(shù)據(jù)的訪問速度。
不過,伴隨分布式以及云計算技術(shù)的不斷發(fā)展,數(shù)據(jù)存儲技術(shù)已然發(fā)生了天翻地覆般的變化。而且,不同的存儲技術(shù)在價格和性能方面均存在極大的差異。所以,在針對性能展開軟件設(shè)計的時候,如果我們未能做好多層級的緩存設(shè)計,不但有可能造成金錢的浪費,而且所獲取的性能收益或許也難以達到理想狀態(tài)。
緩存設(shè)計的通關(guān)之路
那么首先,我打算從兩個問題入手,引領(lǐng)您了解緩存設(shè)計應(yīng)在何時開展,以及通過對不同數(shù)據(jù)類型特性的對比分析,和您共同探討怎樣才能做好緩存設(shè)計。
好,第一個問題:在互聯(lián)網(wǎng)應(yīng)用服務(wù)中,運用緩存技術(shù)的目的難道僅僅是為了提升訪問速度嗎?實際上,我覺得并非所有的緩存都只是為了提速,因為在分布式系統(tǒng)里,緩存機制實則是系統(tǒng)級性能設(shè)計的一項重要權(quán)衡手段。例如,當某個數(shù)據(jù)庫的負載較高,接近系統(tǒng)瓶頸時,我們能夠運用緩存技術(shù),將負荷分攤至其他數(shù)據(jù)庫中,在此,使用緩存的目的,主要是實現(xiàn)負載均衡,而非提升訪問速度。
第二個問題:一個大型系統(tǒng)中的數(shù)據(jù)種類繁多,那么是否需要為每種數(shù)據(jù)都規(guī)劃緩存機制呢?其實完全沒必要。在實際的業(yè)務(wù)場景中,系統(tǒng)所包含的業(yè)務(wù)數(shù)據(jù)極多,您不可能針對每種數(shù)據(jù)都去設(shè)計和實現(xiàn)緩存機制,一方面是投入的軟件成本過高,另一方面也很可能無法帶來較高的性能收益。所以,在開展緩存設(shè)計之前,您首先需要辨別出哪些數(shù)據(jù)訪問對性能的影響較大。那么我們應(yīng)當如何去辨別哪些數(shù)據(jù)需要緩存機制呢?
不過接下來,在識別出需要運用緩存機制的數(shù)據(jù)之后,您或許會發(fā)現(xiàn),這些數(shù)據(jù)種類之間的特性差異極大,如果采用同一種緩存設(shè)計,實際上很難讓軟件性能達到最佳狀態(tài)。所以在此,我為您總結(jié)了三種需要緩存機制的數(shù)據(jù)種類,分別是不變性數(shù)據(jù)、弱一致性數(shù)據(jù)、強一致性數(shù)據(jù)。了解這三種緩存數(shù)據(jù)種類的差異,以及對應(yīng)的設(shè)計緩存機制的方法,您就掌握了緩存設(shè)計的核心要義。
好,下面我們就來具體了解下吧。
首先是不變性數(shù)據(jù)。不變性數(shù)據(jù)意味著數(shù)據(jù)永遠不會發(fā)生變化,或者在相當長的一段時間內(nèi)不會發(fā)生變化,所以我們也能夠認定這部分數(shù)據(jù)是不變的。此類數(shù)據(jù)屬于可以優(yōu)先考慮運用緩存技術(shù)的一種數(shù)據(jù)類型,在實際的業(yè)務(wù)場景中數(shù)量眾多。例如,Web 服務(wù)里的靜態(tài)網(wǎng)頁、靜態(tài)資源,或者數(shù)據(jù)庫表中列數(shù)據(jù)與 key 的映射關(guān)系、業(yè)務(wù)的啟動配置等等,這些都能夠被視為不變性數(shù)據(jù)。
而且,不變性數(shù)據(jù)還意味著實現(xiàn)分布式一致性會極為容易,我們能夠為這些數(shù)據(jù)任選數(shù)據(jù)存儲方式,也能夠任選存儲節(jié)點位置。故而,我們實現(xiàn)緩存機制的方式能夠十分靈活,也會相對簡單。比如在 Java 語言中,您可以直接使用內(nèi)存 Caffeine,或者內(nèi)置的結(jié)構(gòu)體當作緩存均可。
另外這里需要您注意,當您針對不變性數(shù)據(jù)進行緩存設(shè)計時,其中的緩存失效機制可以采用永遠不失效,或者基于時間的失效方式。而在采用基于時間的失效方式時,您還需要依據(jù)具體的業(yè)務(wù)需求,在緩存容量和訪問速度之間做好設(shè)計實現(xiàn)方面的權(quán)衡。
弱一致性數(shù)據(jù)
第二種是弱一致性數(shù)據(jù)。它表示數(shù)據(jù)經(jīng)常會發(fā)生變化,然而業(yè)務(wù)對于數(shù)據(jù)的一致性要求并不高,也就是說,不同用戶在同一時間點看到并非完全一致的數(shù)據(jù),都是能夠被接受的。鑒于這類數(shù)據(jù)對一致性的要求相對較低,所以在設(shè)計緩存機制時,您只需達成最終一致性即可。這類數(shù)據(jù)在實際業(yè)務(wù)里也較為常見,例如業(yè)務(wù)的歷史分析數(shù)據(jù)、一些搜索查找返回的數(shù)據(jù)等,即便最近的部分數(shù)據(jù)未被記錄進去,影響也不大。
另外,快速辨別這類數(shù)據(jù)還有一個辦法,那便是使用從數(shù)據(jù)庫 Replica(復(fù)制)節(jié)點中讀取的數(shù)據(jù),其中大部分都屬于這種類型的數(shù)據(jù)(很多數(shù)據(jù)庫 Replica 節(jié)點的數(shù)據(jù)由于數(shù)據(jù)同步存在時延,是不滿足強一致性要求的)。針對弱一致性的數(shù)據(jù),我們通常采用的緩存失效機制是基于時間的失效方式,同時由于弱一致性的特點,您能夠較為靈活地選擇數(shù)據(jù)存儲技術(shù),比如內(nèi)存 Cache,或者是分布式數(shù)據(jù)庫 Cache。您甚至能夠基于負載均衡的調(diào)度,來設(shè)計多層級緩存機制。
強一致性數(shù)據(jù)
第三種緩存數(shù)據(jù)類型是強一致性數(shù)據(jù)。其指的是數(shù)據(jù)會頻繁發(fā)生變化,并且業(yè)務(wù)對數(shù)據(jù)庫的一致性要求極高,也就是說當數(shù)據(jù)產(chǎn)生變更后,其他用戶在系統(tǒng)內(nèi)的任何地方,都應(yīng)當看到的是更新后的數(shù)據(jù)。
那么,對于這種類型的數(shù)據(jù),我通常不建議您使用緩存機制,因為這類數(shù)據(jù)運用緩存會較為復(fù)雜,并且極易引入新的問題。例如,用戶能夠直接提交和修改的各類數(shù)據(jù)內(nèi)容,如果未同步修改緩存中的數(shù)據(jù),就會引發(fā)數(shù)據(jù)不一致性的問題,導(dǎo)致較為嚴重的業(yè)務(wù)故障。
不過在某些特殊的業(yè)務(wù)場景中,比如,在個別數(shù)據(jù)訪問頻率極高的情況下,我們依舊需要通過設(shè)計緩存機制,來進一步提高性能。
因此針對這類強一致性數(shù)據(jù),在設(shè)計緩存機制時,您需要尤其留意兩點:
這種數(shù)據(jù)的緩存必須采用修改同步的實現(xiàn)方式。也就是說,所有的數(shù)據(jù)修改都必須保證能夠同步修改緩存與數(shù)據(jù)庫中的數(shù)據(jù)。
精確識別特定業(yè)務(wù)流程中,能夠使用緩存獲取數(shù)據(jù)的時長。因為有些緩存數(shù)據(jù)(比如一次 REST 請求中,多個流程都需要使用的數(shù)據(jù))只能夠在單次業(yè)務(wù)流程中使用,不能跨業(yè)務(wù)流程使用。
好了,以上便是三種典型的數(shù)據(jù)種類的緩存設(shè)計思路了。
這里您需要注意的是,使用緩存必定是以性能優(yōu)化為目的,因此,您還需要運用評估模型來分析緩存是否達成了性能優(yōu)化的目標。
那么具體是何種評估模型呢?我們來看一下這個性能評估模型的公式:AMAT = Thit + MR * MP。其中:AMAT(Average Memory Access Time),代表的是平均內(nèi)存訪問時間;Thit,指的是命中緩存之后的數(shù)據(jù)訪問時間;MR,是訪問緩存的失效率;MP,是指緩存失效后,系統(tǒng)訪問緩存的時間與訪問原始數(shù)據(jù)請求的時間之和。
另外這里您可能會留意到,AMAT 與原始數(shù)據(jù)訪問之間的差值,代表的就是使用緩存所帶來的訪問速度的提升。而在一些緩存使用不當?shù)膱鼍跋?,增加的緩存機制很可能會造成數(shù)據(jù)訪問速度下降的情況。所以接下來,我就通過真實的緩存設(shè)計案例,來引領(lǐng)您理解如何正確地使用緩存,以此助力您更有效地提升系統(tǒng)性能。
緩存設(shè)計的典型使用場景
好,在開始介紹之前呢,我還想為您說明一下,在真實的業(yè)務(wù)里,緩存設(shè)計的場景實際上有很多,在這里我的目的主要是讓您明晰緩存設(shè)計的方法。
因此,我會從兩個較為典型的案例場景著手,引領(lǐng)您理解緩存的運用。
如何做好靜態(tài)頁面的緩存設(shè)計?
在 Web 應(yīng)用服務(wù)中,一個重要的應(yīng)用場景便是靜態(tài)頁面的緩存使用。這里所說的靜態(tài)頁面,指的是在一個網(wǎng)站內(nèi),所有用戶看到的都是相同的頁面,除非重新部署,否則通常不會發(fā)生變更,比如大部分公司官網(wǎng)的首頁封面等等。通常情況下,靜態(tài)頁面的訪問并發(fā)量是比較大的,如果您不運用緩存技術(shù),不但會導(dǎo)致用戶響應(yīng)時延較長,而且會給后端服務(wù)帶來極大的負載壓力。
那么針對靜態(tài)頁面,我們在使用緩存技術(shù)時,可以通過將靜態(tài)緩存放置在距離用戶較近的位置,來降低頁面數(shù)據(jù)在網(wǎng)絡(luò)上的傳輸時延。
現(xiàn)在,我們來看一個針對靜態(tài)頁面使用緩存設(shè)計的示意圖:
圖片
如圖上所示,針對靜態(tài)網(wǎng)頁,首先您就能夠在軟件后端服務(wù)的實例中運用緩存技術(shù),以此避免每次都要重新生成頁面信息。然后,由于靜態(tài)網(wǎng)頁屬于不變性數(shù)據(jù),所以您可以使用內(nèi)存或文件級緩存。
另外,針對訪問量極大的靜態(tài)頁面,為了更進一步減輕對后端服務(wù)的壓力,您還能夠?qū)㈧o態(tài)頁面置于網(wǎng)關(guān)處,接著利用 OpenResty 等第三方框架增添緩存機制,以保存靜態(tài)頁面。
除此之外,在網(wǎng)頁中眾多的靜態(tài)頁面或靜態(tài)資源文件,還需要使用瀏覽器的緩存,來進一步提高性能。注意,這里我并非是建議您針對所有的靜態(tài)頁面,都需要設(shè)計三層的緩存機制,而是您要知曉,在軟件設(shè)計階段,通常就需要考慮如何進行靜態(tài)頁面的緩存設(shè)計了
后端服務(wù)如何設(shè)計數(shù)據(jù)庫的多級緩存機制?
還有一個典型的緩存場景是針對數(shù)據(jù)庫的緩存。現(xiàn)在的數(shù)據(jù)庫通常都是分布式存儲的,而且規(guī)模都比較大,在針對大規(guī)模數(shù)據(jù)進行查詢與分析計算時,都需要花費一定的時間周期。因此,我們可以先識別出這些計算結(jié)果中可以使用緩存機制的數(shù)據(jù),然后就可以使用緩存來提升訪問速度了。下面是一張針對數(shù)據(jù)庫緩存機制的原理圖:
圖片
從圖上能夠看到,內(nèi)存級 Cache、分布式 Cache 均可充當數(shù)據(jù)計算分析結(jié)果的緩存。而且,不同級別的緩存訪問速度存在差異,內(nèi)存級的 Cache 訪問速度能夠達到微秒級別,甚至更優(yōu);分布式 Cache 訪問速度通常小于毫秒級別;而對于原生數(shù)據(jù)庫的查詢與分析,通常大于毫秒級別。
因此,在具體規(guī)劃緩存機制的時候,您就需要依照前面我所介紹的緩存使用原理,辨別出數(shù)據(jù)類型,進而選擇并設(shè)計緩存實現(xiàn)機制。
另外,在通過緩存機制實現(xiàn)訪問速度優(yōu)化的過程中,我們主要關(guān)注的是不同層級緩存所帶來的訪問速度提升,并且在此處,不同層級緩存也能夠存在于一個數(shù)據(jù)庫中。
比如,在我參與設(shè)計的一個性能優(yōu)化項目里,其 Cache 策略便是,使用 MongoDB 中的另外一個 Collection(集合),作為緩存查詢分析,以此來優(yōu)化性能。所以,您在進行緩存設(shè)計時,關(guān)注點應(yīng)當置于不同的數(shù)據(jù)種類,以及不同層級緩存的性能評估模型上,而非僅僅關(guān)注數(shù)據(jù)庫。只有如此,您才能夠設(shè)計出更出色、更優(yōu)良的性能緩存方案。