建設(shè)廳官方網(wǎng)站網(wǎng)絡(luò)營銷軟件大全
文章目錄
- 引言
- 什么時候適合加緩存?
- 示例1
- 示例2:
- 示例3:
- 緩存應(yīng)該怎么配置?
- 數(shù)據(jù)分布
- **緩存容量大小:**
- 數(shù)據(jù)淘汰策略
- 緩存的副作用
- 總結(jié)
引言
??在上一篇文章IO密集型服務(wù)提升性能的三種方法中,我們提到了三種優(yōu)化IO密集型系統(tǒng)的方法,其中添加緩存(cache)的方法是最常用的,而且普適性也是最強(qiáng)的,今天展開講下如何正確使用緩存。準(zhǔn)確說我們需要解決下面三個大問題。
- 什么樣的情況下才適合加緩存?
- 緩存應(yīng)該怎么配置?
- 如何解決或者減少緩存的副作用?
什么時候適合加緩存?
??我們先解決第一個問題,什么情況下適合加緩存。 一句話總結(jié)就是**如果某個數(shù)據(jù)獲取”成本高“,并且數(shù)據(jù)是長期有價值,那么這份數(shù)據(jù)是可以加緩存的。**至于這個”長期“到底有多長,我們會在后文中詳細(xì)講解到。 注意 獲取成本高 并且 長期有價值, 這兩個條件是加緩存的必要條件,必須滿足這兩個條件才可以添加緩存。成本高和長期有價值這兩個詞很抽象,怎么判定成本高?什么叫長期有價值?其實這里沒有客觀的判定標(biāo)準(zhǔn),只能是通過對比的方式, 我們拿兩個具體的示例來說明下。
示例1
??你登陸到微信后,你好友的頭像需要展示到微信各個頁面里(聊天、點贊、評論……),頭像是一張圖片,圖片的獲取成本相對于純文本幾十個字節(jié)的數(shù)據(jù)是比較高的,畢竟即便是一張縮略圖也有幾十KB大小,這里就體現(xiàn)出了圖片數(shù)據(jù)相對的成本高。而微信頭像很少會變化,拉取一次長期有效,這里就體現(xiàn)出來了長期有價值的特性。這個場景就很符合可以緩存的數(shù)據(jù)的特點。事實上微信確實也是這么做的,你換個新頭像然后去好友那邊觀察,那邊也不會立即生效的。
示例2:
??可能家庭條件好的同學(xué)都在雙十一買過東西吧,你看中的某個商品庫存信息,它是一直在變化的,你現(xiàn)在看到是100但可能下一秒就變成0了。因為這個數(shù)據(jù)一直是變化的,這個數(shù)據(jù)對時效性的要求很嚴(yán)格,所以它不滿足”長期有價值“這個條件,因此它是無法緩存的。
示例3:
??比如我要掃一遍我們系統(tǒng)里所有內(nèi)容數(shù)據(jù),找出其中不合規(guī)的內(nèi)容,對于單條的內(nèi)容判定是很消耗資源,而且判斷結(jié)果之后也是一直有效的。這種情況下也沒有必要加緩存,因為判定結(jié)果我使用一次之后也不會再用了,這里數(shù)據(jù)不符合長期有價值的特性。 因此這里也是沒必要加緩存的。
??有些數(shù)據(jù)單次獲取的成本不高,但獲取頻次非常高,這種總的獲取成本非常高其實也算是獲取成本高。比如各系統(tǒng)里和人員有個的信息,這種其實一個接口也能快速返回,但架不住調(diào)用的次數(shù)多,這種情況下也是可以添加緩存來減輕壓力的。 我們從獲取成本和長期價值兩個維度可以將數(shù)據(jù)請求拆分到4個象限。
??通過 獲取成本高 并且 長期有價值 這兩個維度,你可以想想在你所遇到的所有業(yè)務(wù)場景中,有哪些是可以通過加緩存來提升性能的?
緩存應(yīng)該怎么配置?
??在解決了什么場景下可以加緩存的時候,就得考慮怎么配置緩存了。這里先來介紹下一個如何評估緩存性能的公式,為了直觀點我們以IO密集型場景舉例,IO密集型衡量性能的一個關(guān)鍵指標(biāo)就是訪問延遲(latency),在緩存的加持下,整個系統(tǒng)的平均延時計算方式如下:
a v g L a t e n c y = h i t R a t e × c a c h e L a t e n c y + ( 1 ? h i t R a t e ) × o r i g i n a l L a t e n c y (1) avgLatency = hitRate \times cacheLatency + (1 - hitRate) \times originalLatency \tag{1} avgLatency=hitRate×cacheLatency+(1?hitRate)×originalLatency(1)
??其中 hitRate 代表的是緩存的命中率,cacheLatency是訪問一次緩存時的延時,originalLatency 是訪問一次原始數(shù)據(jù)時的延遲。avgLatency就是我們系統(tǒng)最終的延遲,這個值越低越好。 有些同學(xué)可能會問,為什么我們不直接把數(shù)據(jù)全部放到訪問最快的存儲介質(zhì)中? 這里就涉及到不同存儲介質(zhì)的特性了,主要是其數(shù)據(jù)訪問速度、價格導(dǎo)致,總之越快的存儲介質(zhì),單位價格也就越高,這里就不在展開了,大家可以自行查閱下資料。
??在我們選定一種緩存介質(zhì)時,緩存延遲(cacheLatency)也就基本固定下來了,而原始數(shù)據(jù)的訪問延遲(originalLatency)我們肯定是干預(yù)不了的。剩下我們能干預(yù)的也只有緩存命中率(hitRate)。我們以redis作為緩存為例,然后緩存某個10ms接口的數(shù)據(jù),同機(jī)房內(nèi)redis訪問延遲是0.1ms的量級 當(dāng)緩存命中率分別為1% 10% 50% 90% 100%時,我們用上面公式計算出來的結(jié)果分別是 9.9ms 9ms 5ms 1ms 0.1ms??梢钥闯鲈诓煌木彺婷新氏伦罱K結(jié)果也有著巨大的差異,命中率越高性能越強(qiáng)。
??由于成本限制,我們一般是不會將全量的數(shù)據(jù)導(dǎo)入到緩存介質(zhì)里的,所以大部分情況下緩存命中率不會是100%,而且還希望緩存空間用的越少越好。如何在有限的緩存介質(zhì)下,提升系統(tǒng)性能? 從上面的公式就可以看出,提升緩存性能最關(guān)鍵的就是提升緩存的命中率,而緩存的命中率和數(shù)據(jù)的訪問分布、緩存的大小、緩存數(shù)據(jù)淘汰策略息息相關(guān)。
**數(shù)據(jù)分布:**數(shù)據(jù)訪問是否有規(guī)律,是否可以利用這些規(guī)律?
**緩存大小:**指緩存最多能存儲多少的數(shù)據(jù)。
**數(shù)據(jù)淘汰策略:**在緩存已滿的情況下,如何剔除緩存中價值最低的數(shù)據(jù),騰出空間來給別的數(shù)據(jù)使用。
數(shù)據(jù)分布
??當(dāng)我將一份單位時間內(nèi)訪問頻次標(biāo)記在縱坐標(biāo)軸上,并且按頻次從大到小排序。大部分情況下我們會的到以上這張數(shù)據(jù)訪問分布圖,可以看出最左側(cè)的一部分?jǐn)?shù)據(jù)占掉了總體訪問非常大的一部分(頭部效應(yīng)),而且長尾的大量數(shù)據(jù)被訪問的頻次很低。這就是大家所熟知的長尾分布。 這種分布下的數(shù)據(jù)是最時候加緩存的,頭部效應(yīng)越明顯加緩存后的性能提升越明顯,幸運的是在現(xiàn)實世界中,絕大多數(shù)數(shù)據(jù)的訪問分布都是這樣的。
??當(dāng)然也有部分?jǐn)?shù)據(jù)在單位時間內(nèi)訪問的頻次很均勻,這種一般都是程序定時觸發(fā)的數(shù)據(jù)訪問,這種情況下添加緩存帶來的效果提升就很弱,甚至沒有提升。 所以你會看到在定時任務(wù)里,很少會考慮去加緩存。
緩存容量大小:
??在確定了數(shù)據(jù)分布后,就得考慮緩存容量大小的問題了。從上面的性能計算公式來看,理論上緩存容量越大,緩存對性能提升就越明顯,但上文我也提到了越快的存儲介質(zhì)價格越高。在上圖中,藍(lán)色框相比紅色框在容量增加一倍后,其覆蓋的數(shù)據(jù)訪問(曲線下方帶背景色面積)遠(yuǎn)沒有增加一倍,后面增加緩存帶來的覆蓋率增長也會越來越低。
??可以看出,緩存容量大小也會顯著影響到緩存的命中率,從而影響到性能提升。反過來,我們在知道預(yù)期性能目標(biāo)后,也可以根據(jù)上面的公式反向去計算預(yù)期緩存命中率,然后計算出緩存的最小合理配置大小。
數(shù)據(jù)淘汰策略
??當(dāng)緩存已滿時,我們就需要考慮如何淘汰出當(dāng)前緩存下最沒有價值的數(shù)據(jù),也就是未來最不可能被訪問的數(shù)據(jù)。 沒有任何人或者系統(tǒng)擁有準(zhǔn)確預(yù)知未來的能力,但我們有個簡單策略來估算每份數(shù)據(jù)未來可能被使用的概率,這個策略背后的依據(jù)就是局部性,如果某個數(shù)據(jù)被訪問了,那么它未來被訪問的概率會高于其他未被訪問的數(shù)據(jù)。已被訪問的頻次越高,未來再被訪問的可能性也就越高。所以這里我們只需要記錄下每份數(shù)據(jù)被訪問的頻次,然后把緩存中被訪問頻次最低的一份數(shù)據(jù)刪除掉就行。
??說到這里我們已經(jīng)不小心發(fā)明了LFU (Least Frequently Used)緩存淘汰策略,即維護(hù)每份數(shù)據(jù)的訪問頻次,當(dāng)緩存空間不足時,淘汰訪問頻次最少的一份數(shù)據(jù)。 與之齊名了還有LRU (Least Recently Used),LRU每次淘汰緩存中最長沒有被使用過的數(shù)據(jù),具體實現(xiàn)的時候按數(shù)據(jù)被訪問的時間由近到遠(yuǎn)排成一個隊列,然后淘汰最尾的一份數(shù)據(jù),相對與LFU實現(xiàn)會簡單很多。
??LFU和LRU在一定程度上都實現(xiàn)了淘汰最低價值數(shù)據(jù)的功能,但還是有一些差異的。
- LFU (Least Frequently Used): 這種策略會淘汰在一段時間內(nèi)被訪問次數(shù)最少的數(shù)據(jù)。這意味著即使某個數(shù)據(jù)項最近被訪問過,但只要其總的訪問次數(shù)仍然是最少的,那么就會被淘汰。LFU適合的場景是存在長期熱點數(shù)據(jù)的情況,也就是說有一部分?jǐn)?shù)據(jù)的訪問頻率持續(xù)地高于其他數(shù)據(jù)。
- LRU (Least Recently Used): 這種策略會將最近最少使用的數(shù)據(jù)項淘汰。也就是說,如果一個數(shù)據(jù)項在過去被訪問的頻率較低,那么在需要淘汰數(shù)據(jù)以騰出空間時,這個數(shù)據(jù)項會被優(yōu)先考慮。LRU比較適合在數(shù)據(jù)訪問模式中存在明顯的"最近偏好"特性的情況,即最近訪問過的數(shù)據(jù)在未來被再次訪問的概率相對較高。
??總的來講,LFU能涵蓋的數(shù)據(jù)時間窗口更大,而LRU只能覆蓋緩存大小那么大的時間窗口,所以LFU更適合大時間窗口下頭部效應(yīng)明顯的數(shù)據(jù),而LRU更適合小時間窗口下頭部效應(yīng)明顯的數(shù)據(jù)。根據(jù)我的使用經(jīng)驗,LRU大部分情況下足以。
??除了LRU和LFU之外,還有其他的數(shù)據(jù)淘汰策略,比如FIFO和Random,甚至還有一些LRU和LFU的改進(jìn)策略,核心都是為了提升緩存的命中率,在具體選擇的時候,還是需要根據(jù)自己的緩存大小和數(shù)據(jù)分布,選擇合適的數(shù)據(jù)淘汰策略。
緩存的副作用
??緩存最大的副作用就是數(shù)據(jù)的不一致性,這也是很多人談緩存色變,甚至成為拒絕使用緩存的理由。確實使用緩存會有數(shù)據(jù)不一致性的可能,但實際上很多系統(tǒng)是可以接受短暫的數(shù)據(jù)不一致性的。就像上文中微信頭像的問題,實測更換微信頭像后,甚至幾天后別人看到的還是你的舊頭像。不是微信沒法解決頭像一致性的問題,而是這個問題價值并不大,很多人都能接受。
??避免數(shù)據(jù)一致性的問題分為被動和主動兩種方式,被動方式就是給數(shù)據(jù)設(shè)置有效期,像大家在使用redis緩存或者spring-cache時,都是可以設(shè)置數(shù)據(jù)過期時間的。當(dāng)然數(shù)據(jù)過期時間的設(shè)置也是有講究的,設(shè)大了不僅浪費空間,而且還會增加讀到就數(shù)據(jù)的概率。 命中過期數(shù)據(jù)的概率可以用以下公式計算:
i n v a l i d H i t R a t e = c a c h e E x p i r e T i m e 2 d a t a U p d a t e I n t e r v a l × c a c h e H i t R a t e (2) invalidHitRate= \frac{\frac{cacheExpireTime}{2}} {dataUpdateInterval} \times cacheHitRate \tag{2} invalidHitRate=dataUpdateInterval2cacheExpireTime??×cacheHitRate(2)
??其中dataUpdateInterval指的是數(shù)據(jù)的更新時間間隔,比如某些數(shù)據(jù)平均3天更新一次,這里就是3天。cacheExpireTime就是你在設(shè)置緩存的時候數(shù)據(jù)的有效期,任意時刻下剩余有效期平均就是原來的1/2。而cacheHitRate就是上文中提到的緩存命中率。 在開接受的錯誤率下,根據(jù)緩存命中率和數(shù)據(jù)更新的周期,就可以反推出一個合理的緩存數(shù)據(jù)有效期。 這里需要注意的時,對緩存數(shù)據(jù)設(shè)置過期時間也可能會顯著影響緩存的命中率,最終緩存的命中率需要綜合緩存大小、數(shù)據(jù)淘汰策略和緩存過期時間綜合去看。
??另外一種主動的方式就是在檢測到數(shù)據(jù)發(fā)生變化后,主動刪除緩存里的數(shù)據(jù),或者是主動將變更后的數(shù)據(jù)寫入。相比被動的方式,錯誤數(shù)據(jù)的命中率會顯著降低。計算錯誤數(shù)據(jù)命中率時,將公式2中的cacheExpireTime替換為數(shù)據(jù)變更處理時間即可,這在實際使用中這部分時間幾乎是0。 但主動更新的方式有很高的實現(xiàn)復(fù)雜度,首先數(shù)據(jù)需要有變更事件通知的能力,這在很多系統(tǒng)里都是需要額外開發(fā)的。其次還需要實現(xiàn)變更監(jiān)聽和寫入的邏輯,又帶來了額外的開發(fā)量。 所以除非是很重要的數(shù)據(jù),一般不會選擇主動更新的方式。
總結(jié)
??在本文中,我們探討了正確使用緩存以提升系統(tǒng)性能的關(guān)鍵要素。首先,我們通過數(shù)據(jù)獲取成本和長期價值兩個維度來確定是否適合添加緩存。然后,我們深入討論了緩存配置的策略,包括如何根據(jù)訪問延遲、命中率以及存儲成本來評估和優(yōu)化緩存表現(xiàn)。其中緩存命中率是性能提升的核心,而這與數(shù)據(jù)訪問的分布、緩存的大小和數(shù)據(jù)的淘汰策略緊密相關(guān)。最后,我們討論了緩存帶來的主要副作用:數(shù)據(jù)一致性問題。我們介紹了兩種處理這一問題的方法,一種是被動的設(shè)置數(shù)據(jù)有效期,另一種是主動的數(shù)據(jù)更新策略,每種方法都有其適用場景和權(quán)衡考量。
??緩存是一個強(qiáng)大的工具,用好的話還是可以顯著提升系統(tǒng)性能的。選擇是否以及如何使用緩存需要從數(shù)據(jù)特性、業(yè)務(wù)需求和成本收益上綜合去考慮。正確的配置和管理可以最大化緩存的優(yōu)勢,同時降低潛在的風(fēng)險。