由淺入深學習分布式緩存

引入

在分布式的系統(tǒng)中,很多時候的所謂的性能優(yōu)化,其實就是一個如何使用緩存的過程净刮。緩存這個東西剥哑,說起來簡單,但是真用起來需要考慮的面卻多種多樣淹父,本文將由淺入深講述緩存使用中遇到的各種問題株婴,它們是如何出現(xiàn)的,并且提供解決辦法暑认。

為什么要緩存

很多時候我們進行一個復雜處理困介,這個復雜的處理可能需要耗費比較長的時間,并且這個結果可能并不會經(jīng)常地發(fā)生改變穷吮,如果我們每次請求過來,都去執(zhí)行一次這樣復雜的計算饥努,對于系統(tǒng)性能的耗費是極其昂貴的捡鱼,一個好的做法是記錄下上一次計算的值,然后下一次請求來的時候直接返回上次記錄的值酷愧,這樣就可以極大地避免復雜計算驾诈,并提升系統(tǒng)響應性能。

緩存的狀態(tài)

我們學習緩存的使用溶浴,經(jīng)常會有什么緩存擊穿乍迄,緩存預熱等概念,這些概念本質(zhì)上都是不同場景下大量緩存處于失效狀態(tài)導致的士败。嚴格來說闯两,緩存只有兩個狀態(tài) 有效失效有效狀態(tài)就是指:緩存中有值可以用的狀態(tài)谅将。失效狀態(tài)指的是緩存中無值可用的狀態(tài)漾狼。失效狀態(tài)很重要,因為使用緩存的核心就是處理緩存的失效狀態(tài)饥臂。

緩存主動失效的必要性

我們緩存的數(shù)據(jù)源逊躁,根據(jù)是否會隨著時間改變發(fā)生變化,分為如下兩種 case

數(shù)據(jù)變化的場景

假設當前我們緩存了數(shù)據(jù)庫中的數(shù)據(jù)隅熙,但是數(shù)據(jù)庫的數(shù)據(jù)不會一成不變稽煤,他們是會自己變更的,但是數(shù)據(jù)庫變更操作一般來說是不會通知到緩存的(當然你也可以在變更數(shù)據(jù)庫的時候更新緩存)囚戚,所以這個時候如果你的緩存沒有主動失效的機制酵熙,那么用戶看到的數(shù)據(jù)永遠不會得到更新,這是無法接受的驰坊。

數(shù)據(jù)不變的場景

假設我們的后端方法是一個 pure function 函數(shù)绿店,也就是說任何時間對于同一個輸入只有唯一返回結果的時候,那么我們緩存基本上是可以是沒有過期時間的。但是這里也是有區(qū)別的假勿,如果 pure function 需要緩存的計算結果很多的時候借嗽,那么緩存過多的結果,會耗費過多的緩存空間转培,所以我們還是需要設定緩存的上限恶导,當緩存值超過一定上限的時候,我們還是需要主動失效一些緩存的浸须,具體如何失效惨寿,一般有如下算法

  • FIFO 算法:先進先出算法。
  • LFU 算法:最少使用算法
  • LRU 算法:最近最少使用算法删窒。

具體算法的內(nèi)容裂垦,本文不贅述,大家可以自行學習肌索。接下來我們思考返回結果可以窮舉蕉拢,并且數(shù)量不大的情況,如果是這種情況诚亚,那么這個問題退化成一個固定配置晕换。一般來說,我們不認為這是一個緩存的問題站宗。

綜上所述闸准,無論是后端數(shù)據(jù)不可變,還是可變的場景梢灭,讓緩存能主動失效是十分有必要的夷家。

緩存失效問題

在上文中,我已經(jīng)提到敏释,大部分緩存問題都是由于大量請求過來瘾英,而緩存卻處于失效狀態(tài)導致的,而緩存失效的目的是為了能讓緩存得到更新颂暇。所以我們先從緩存更新說起

緩存更新使用方法

一般來說缺谴,更新緩存有兩種方法,一種是寫更新耳鸯,一種讀更新

寫更新

寫更新湿蛔,一般用于我們有權限處理后端數(shù)據(jù)情況,比如操作數(shù)據(jù)庫的情況县爬,當我們更新數(shù)據(jù)的值成功的時候阳啥,我們可以主動的將數(shù)據(jù)庫中的新值寫入。偽代碼如下

    update(userInfo);
    cache.put(userInfo.getId(),userInfo);

在這段偽代碼中财喳,我們的緩存是一個 Map 結構察迟,key 為用戶的 id斩狱,value 為用戶信息。當我們執(zhí)行 put 之后扎瓶,其余讀請求就能立馬讀取到最新緩存的數(shù)據(jù)所踊。(這里的 cache 一般是需要使用 volatile 的,為了能保證數(shù)據(jù)的立即可見性概荷,這個內(nèi)容見 《java 并發(fā)編程》本文不贅述) 寫更新的好處是秕岛,數(shù)據(jù)一更新緩存就更新了,不過寫更新要求你能獲知原始數(shù)據(jù)的更新狀態(tài)误证。但是如果數(shù)據(jù)源你無法直接獲知后端數(shù)據(jù)的變更狀態(tài)继薛,便無法使用該方案。

讀更新

讀更新是使用最多的方案愈捅,在讀更新方案中遏考,緩存數(shù)據(jù)一般會有一個新鮮度,如果新鮮度過低蓝谨,則會觸發(fā)去后端獲取數(shù)據(jù)的操作灌具,否則直接使用緩存的值。(這個新鮮度根據(jù)需求自己定義就好像棘,一般設置為一個過期時間稽亏。)偽代碼如下

    cahceValue = cache.get(key);
    if(cahceValue.isFresh()){
        return cahceValue.getValue();
    } else {
        originValue = getData(key);
        cache.put(key,originValue);
        return originValue;
    }

這樣方案使用范圍廣壶冒,當業(yè)務場景對數(shù)據(jù)新鮮度不敏感的時候推薦使用這種方法缕题。事實上及時可以使用寫緩存的操作,大部分情況下依舊會使用讀更新的方式胖腾,這樣有助于當寫更新失敗的時候烟零,讀更新還能在一段時間之后自行更新數(shù)據(jù),提升系統(tǒng)的穩(wěn)定性咸作。

高并發(fā)狀態(tài)下的緩存(緩存并發(fā))

在高并發(fā)的情況下锨阿,我們可能會遇到這么情況,就是現(xiàn)在有一批請求過來請求同一個緩存的值记罚,但是不巧這個時候緩存處于失效狀態(tài)墅诡,根據(jù)讀更新策略,這個時候每個請求都會嘗試去獲取最新的數(shù)據(jù)桐智,這個時候如果后端是數(shù)據(jù)庫的話末早,很容易就把數(shù)據(jù)庫給讀掛了!導致整個系統(tǒng)不可用说庭。

大量請求請求數(shù)據(jù)的情況

普通的緩存更新情況.png

控制對后端數(shù)據(jù)的請求線程數(shù)

我們知道這個時候雖然有很多請求然磷,但是每個請求拿到的數(shù)據(jù)基本上一定一樣的,所以根本沒必要創(chuàng)造這么多的請求刊驴,所以這個時候合適的做法是做并發(fā)控制姿搜,當緩存失效的時候寡润,只有唯一的一個線程去向數(shù)據(jù)獲取數(shù)據(jù),而其它線程在等待結果舅柜。方案如下

等待更新線程的情況.png

圖中梭纹,我們只畫了線程,但是其實應該根據(jù)實際情況去控制線程的數(shù)理和粒度业踢。粒度細的栗柒,比如一個 key 最多只有一個線程。粒度粗一點知举,可以考慮整個緩存只有一個線程用于更新數(shù)據(jù)瞬沦。這個根據(jù)實際業(yè)務量進行調(diào)整。

緩存擊穿(緩存穿透)

在上一個議題中雇锡,我們談了高并發(fā)的情況下逛钻,通過控制實際去獲取數(shù)據(jù)的線程的方法去保證后端的安全。但是如果后端沒有數(shù)據(jù)呢锰提?這個時候不停地向服務器請求指定 key 對應的數(shù)據(jù)曙痘,依舊會不停地觸發(fā)服務器向后端服務發(fā)起請求。這樣雖然只有一個線程立肘,但是依舊會不斷地給后端施加壓力边坤!

空對象

這種場景的解決辦法,是創(chuàng)建一個空對象谅年。然后下次請求來的時候茧痒,直接返回這個空對象,防止請求擊穿到后端服務融蹂。

    originValue = getData(key);
    if(originValue == null){
        cache.put(key,new Object());
    }

緩存預熱

一般系統(tǒng)啟動的時候旺订,緩存會由讀更新的方法,逐步寫入到系統(tǒng)中超燃。但是如果這個過程特別快区拳,一瞬間有大量的請求過來,但是這個時候大部分緩存還沒有 ready 意乓,這依舊會導致大量請求擊穿緩存樱调,直接訪問到后端服務,壓垮后端服務届良。

提前加載緩存數(shù)據(jù)

這種問題的解決方案是在應用提供服務前笆凌,先把數(shù)據(jù)的一部分寫入緩存,然后再提供服務伙窃,這就叫緩存預熱.

優(yōu)化并發(fā)緩存響應性能的一種思路

在剛剛緩存并發(fā)的時候菩颖,我們提到可以通過控制向后端請求的線程的方式來保護我們的后端服務,但是回過頭來看我們自身为障,如果這個時候后端服務性能較差晦闰,那么會導致大量請求線程被阻塞放祟,這樣有可能后端服務還沒有掛,我們的服務先掛了呻右,即使沒有掛也會讓大量的用戶請求超時跪妥。

高響應性能的緩存策略.png

我們重新思考我們的業(yè)務場景,一般來說能使用讀緩存的業(yè)務場景声滥,對緩存的時效性都不會有太高的要求眉撵,如果當緩存到達失效期的時候,我們還能拿到舊值落塑,這個時候如果已經(jīng)有線程在向后端發(fā)起請求纽疟,這個時候我們不去等待線程的返回結果,而是直接使用舊的數(shù)據(jù)返回憾赁,這樣系統(tǒng)的響應性就能獲得極大的提高污朽,因為對于用戶請求來說,他們總是立即就獲得值了龙考。

總結

任何時候技術和業(yè)務都是要相互平衡的蟆肆,很多緩存策略能否執(zhí)行,一部分還要看業(yè)務是否能接受數(shù)據(jù)暫時使用舊值而不是最新的值晦款,另一部分才是系統(tǒng)設計炎功。我們設計系統(tǒng)的時候,要有兩個視角缓溅,一個是發(fā)送請求者蛇损,我們要注意不要壓垮我們的依賴方,另一方面我們也是服務提供方肛宋,要思考如何自身不被拖垮州藕。

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末束世,一起剝皮案震驚了整個濱河市酝陈,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌毁涉,老刑警劉巖沉帮,帶你破解...
    沈念sama閱讀 206,839評論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異贫堰,居然都是意外死亡穆壕,警方通過查閱死者的電腦和手機其屏,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評論 2 382
  • 文/潘曉璐 我一進店門喇勋,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人偎行,你說我怎么就攤上這事川背》∧茫” “怎么了?”我有些...
    開封第一講書人閱讀 153,116評論 0 344
  • 文/不壞的土叔 我叫張陵熄云,是天一觀的道長膨更。 經(jīng)常有香客問我,道長缴允,這世上最難降的妖魔是什么荚守? 我笑而不...
    開封第一講書人閱讀 55,371評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮练般,結果婚禮上矗漾,老公的妹妹穿的比我還像新娘。我一直安慰自己薄料,他們只是感情好缩功,可當我...
    茶點故事閱讀 64,384評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著都办,像睡著了一般嫡锌。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上琳钉,一...
    開封第一講書人閱讀 49,111評論 1 285
  • 那天势木,我揣著相機與錄音,去河邊找鬼歌懒。 笑死啦桌,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的及皂。 我是一名探鬼主播甫男,決...
    沈念sama閱讀 38,416評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼验烧!你這毒婦竟也來了板驳?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,053評論 0 259
  • 序言:老撾萬榮一對情侶失蹤碍拆,失蹤者是張志新(化名)和其女友劉穎若治,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體感混,經(jīng)...
    沈念sama閱讀 43,558評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡端幼,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,007評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了弧满。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片婆跑。...
    茶點故事閱讀 38,117評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖庭呜,靈堂內(nèi)的尸體忽然破棺而出滑进,到底是詐尸還是另有隱情摹迷,我是刑警寧澤,帶...
    沈念sama閱讀 33,756評論 4 324
  • 正文 年R本政府宣布郊供,位于F島的核電站峡碉,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏驮审。R本人自食惡果不足惜鲫寄,卻給世界環(huán)境...
    茶點故事閱讀 39,324評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望疯淫。 院中可真熱鬧地来,春花似錦、人聲如沸熙掺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,315評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽币绩。三九已至蜡秽,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間缆镣,已是汗流浹背芽突。 一陣腳步聲響...
    開封第一講書人閱讀 31,539評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留董瞻,地道東北人寞蚌。 一個月前我還...
    沈念sama閱讀 45,578評論 2 355
  • 正文 我出身青樓,卻偏偏與公主長得像钠糊,于是被迫代替她去往敵國和親挟秤。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,877評論 2 345

推薦閱讀更多精彩內(nèi)容