redis學(xué)習(xí)筆記(八) 緩存設(shè)計(jì)

1. 緩存優(yōu)缺點(diǎn)

緩存常用的結(jié)構(gòu)如下:

緩存

1.1. 優(yōu)點(diǎn)

  • 加速讀寫:由于數(shù)據(jù)庫讀寫速度慢,而基于內(nèi)存的讀寫速度快,所以使用緩存可以加速讀寫想幻,優(yōu)化用戶體驗(yàn)

  • 降低后端負(fù)載:幫助后端減少訪問量和復(fù)雜計(jì)算五辽,從而降低了后端的負(fù)載。

1.2 缺點(diǎn)

  • 數(shù)據(jù)不一致:緩存層和存儲(chǔ)層可能存在數(shù)據(jù)不一致的問題诚亚,具體何時(shí)一致和同步更新策略有關(guān)。

  • 代碼維護(hù)成本增加:要同時(shí)維護(hù)緩存層和存儲(chǔ)層

  • 運(yùn)維成本增加

2. 緩存更新策略

2.1 LRU/LFU/FIFO算法剔除

剔除算法通常用于緩存超過預(yù)設(shè)的最大值的時(shí)候午乓,如何對(duì)現(xiàn)有的數(shù)據(jù)進(jìn)行剔除站宗。redis使用maxmemory-policy這個(gè)配置作為內(nèi)存超過預(yù)設(shè)的最大值后對(duì)于數(shù)據(jù)的剔除策略。

2.2 超時(shí)剔除

通過給緩存數(shù)據(jù)設(shè)置過期時(shí)間益愈,讓其在過期時(shí)間后自動(dòng)刪除梢灭,如expire命令。

2.3 主動(dòng)更新

真實(shí)數(shù)據(jù)更新后,立即更新緩存數(shù)據(jù)敏释。

3. 緩存粒度控制

以使用redis+mysql為例:

首先從數(shù)據(jù)庫中獲取用戶信息:

select * from user where id={id}

然后再將數(shù)據(jù)保存到redis中:

set user 'select * from user where id={id}'

這樣表中的全部列的信息都保存到緩存中了库快,但是如果想保存部分列呢:

set user 'select id,name... from user where id={id}'

如果列很多的時(shí)候,這樣一一列舉就不太方便颂暇,導(dǎo)致代碼可維護(hù)性增加缺谴。

緩存全部和緩存部分對(duì)比如下,使用時(shí)要自行取舍:

  • 緩存全部:通用性高耳鸯,占用空間大湿蛔,代碼維護(hù)簡單
  • 緩存部分:通用性低,占用空間小县爬,代碼維護(hù)復(fù)雜

4. 緩存穿透

通常情況下阳啥,處于容錯(cuò)的考慮,根據(jù)key先去緩存層查詢财喳,如果緩存查不到察迟,再去數(shù)據(jù)查詢。如果數(shù)據(jù)庫也查不到數(shù)據(jù)則不寫入緩存層耳高。圖示如下:

屏幕快照 2019-04-15 上午10.40.25.png

如果一些惡意攻擊對(duì)很多此類緩存和存儲(chǔ)層都沒有的值進(jìn)行查詢扎瓶,就會(huì)導(dǎo)致緩存層沒有起到保護(hù)存儲(chǔ)層的效果,大量請(qǐng)求加大數(shù)據(jù)庫負(fù)載泌枪,從而導(dǎo)致宕機(jī)概荷,這就是緩存穿透的后果。

4.1 優(yōu)化

為解決上述緩存穿透問題碌燕,可以使用下述方法進(jìn)行優(yōu)化误证。

4.1.1 緩存空對(duì)象

當(dāng)數(shù)據(jù)庫查不到數(shù)據(jù)時(shí),仍將空對(duì)象保存到緩存中修壕。之后再訪問這個(gè)數(shù)據(jù)時(shí)愈捅,就會(huì)先去訪問緩存,這樣就起到了保護(hù)數(shù)據(jù)庫的作用慈鸠。

屏幕快照 2019-04-15 上午10.47.01.png

緩存空對(duì)象有如下問題:

  • 如果這些空對(duì)象很多的時(shí)候蓝谨,也會(huì)占用過多的redis存儲(chǔ)空間,導(dǎo)致緩存的壓力加大青团,比較有效的方法是像棘,設(shè)置一個(gè)較短的過期時(shí)間,讓其自動(dòng)剔除壶冒。

4.1.2 布隆過濾器攔截

布隆過濾器

將可能出現(xiàn)的緩存key的組合方式的所有數(shù)值以hash形式存儲(chǔ)在一個(gè)很大的bitmap中<布隆過濾器>(需要考慮如何將這個(gè)可能出現(xiàn)的數(shù)據(jù)的hash值之后同步到bitmap中, eg. 后端每次新增一個(gè)可能的組合就同步一次)截歉,一個(gè)一定不存在的數(shù)據(jù)會(huì)被這個(gè)bitmap攔截掉胖腾,從而避免了對(duì)底層存儲(chǔ)系統(tǒng)的查詢壓力。

布隆過濾器類似于將一個(gè)key通過n個(gè)不同的hash函數(shù)定位成n個(gè)整數(shù),然后將這n個(gè)整數(shù)定位在一個(gè)長度在M的初始數(shù)值為0的數(shù)組下標(biāo)上咸作,設(shè)置該n個(gè)下標(biāo)的數(shù)值為1锨阿。 那只要當(dāng)查詢過來,用這n個(gè)hash函數(shù)定位判定都為1那基本就存在记罚,只要有任一下標(biāo)的數(shù)組值不是1墅诡,則代表不存在。

5. 雪崩優(yōu)化

下面描述了什么是緩存雪崩:由于緩存層承載著大量請(qǐng)求桐智,有效地保護(hù)了存儲(chǔ)層末早,但是如果緩存層由于某些原因不能提供服務(wù),于是所有的請(qǐng)求都會(huì)達(dá)到存儲(chǔ)層说庭,存儲(chǔ)層的調(diào)用量會(huì)暴增然磷,造成存儲(chǔ)層也會(huì)級(jí)聯(lián)宕機(jī)的情況。緩存雪崩的英文原意是stampeding herd(奔逃的野牛)刊驴,指的是緩存層宕掉后姿搜,流量會(huì)像奔逃的野牛一樣,打向后端存儲(chǔ)捆憎。

屏幕快照 2019-04-18 上午10.11.00.png

預(yù)防和解決緩存雪崩問題舅柜,可以從以下三個(gè)方面進(jìn)行著手。

1)保證緩存層服務(wù)高可用性 躲惰。和飛機(jī)都有多個(gè)引擎一樣致份,如果緩存層設(shè)計(jì)成高可用的,即使個(gè)別節(jié)點(diǎn)礁扮、個(gè)別機(jī)器知举、甚至是機(jī)房宕掉,依然可以提供服務(wù)太伊,例如前面介紹過的Redis Sentinel和Redis Cluster都實(shí)現(xiàn)了高可用雇锡。

2)依賴隔離組件為后端限流并降級(jí) 。無論是緩存層還是存儲(chǔ)層都會(huì)有出錯(cuò)的概率僚焦,可以將它們視同為資源锰提。作為并發(fā)量較大的系統(tǒng),假如有一個(gè)資源不可用芳悲,可能會(huì)造成線程全部阻塞(hang)在這個(gè)資源上立肘,造成整個(gè)系統(tǒng)不可用。降級(jí)機(jī)制在高并發(fā)系統(tǒng)中是非常普遍的名扛,如Java依賴隔離工具Hystrix谅年。

3)提前演練 。在項(xiàng)目上線前肮韧,演練緩存層宕掉后融蹂,應(yīng)用以及后端的負(fù)載情況以及可能出現(xiàn)的問題旺订,在此基礎(chǔ)上做一些預(yù)案設(shè)定。

6. 緩存擊穿

開發(fā)人員通常使用“緩存+過期時(shí)間”的策略既可以加速數(shù)據(jù)讀寫超燃,又保證數(shù)據(jù)的定期更新区拳,這種模式基本能夠滿足絕大部分需求。但是如果當(dāng)前key是一個(gè)熱點(diǎn)key(例如一個(gè)熱門的娛樂新聞)意乓,并發(fā)量非常大樱调,那這個(gè)key正好到了過期時(shí)間失效了,導(dǎo)致眾多請(qǐng)求都獲取不到這個(gè)原保存到緩存中的key届良,從而全部去請(qǐng)求數(shù)據(jù)庫了笆凌,但是執(zhí)行數(shù)據(jù)庫可能是一個(gè)復(fù)雜計(jì)算,例如復(fù)雜的SQL伙窃、多次IO菩颖、多個(gè)依賴等,就會(huì)瞬間增大數(shù)據(jù)庫的壓力为障,引起數(shù)據(jù)庫服務(wù)器宕機(jī)晦闰,這就是緩存擊穿。

解決方案如下鳍怨。

6.1 互斥鎖

當(dāng)緩存中沒有數(shù)據(jù)的時(shí)候會(huì)去訪問數(shù)據(jù)并重寫到緩存呻右,這個(gè)過程可稱其為重建緩存。

此方法只允許一個(gè)線程重建緩存鞋喇,其他線程等待重建緩存的線程執(zhí)行完声滥,重新從緩存獲取數(shù)據(jù)即可,示例代碼如下:

String get(String key){
    String value = redis.get(key);
    if(value == null){
        String mutexKey = "mutex:key:"+key; // 設(shè)置作為上鎖的key
        if(redis.set(mutexKey,"1","ex 180","nx")){ // 使用setnx上鎖
            value = db.get(key); // 從數(shù)據(jù)庫中獲取
            redis.setex(key,timeout,value); // 重建該key的緩存
            redis.delete(mutexKey); // 解鎖
        } else {
            Thread.sleep(50);
            get(key);
        }
    }
    return value;
}

1)從Redis獲取數(shù)據(jù)侦香,如果值不為空落塑,則直接返回值;否則執(zhí)行下面的2.1)和2.2)步驟罐韩。

2.1)如果set(nx和ex)結(jié)果為true憾赁,說明此時(shí)沒有其他線程重建緩存,那么當(dāng)前線程執(zhí)行緩存構(gòu)建邏輯散吵。

2.2)如果set(nx和ex)結(jié)果為false龙考,說明此時(shí)已經(jīng)有其他線程正在執(zhí)行構(gòu)建緩存的工作,那么當(dāng)前線程將休息指定時(shí)間(這里為50毫秒矾睦,取決于構(gòu)建緩存的速度)后晦款,重新執(zhí)行函數(shù),直到獲取到數(shù)據(jù)枚冗。

使用互斥鎖存在的問題就是通過加鎖阻塞其他調(diào)用方的方式缓溅,可能會(huì)存在死鎖和線程阻塞的風(fēng)險(xiǎn)。

6.2 永遠(yuǎn)不過期

“永遠(yuǎn)不過期”包含兩層意思:

  • 從緩存層面來看赁温,確實(shí)沒有設(shè)置過期時(shí)間肛宋,所以不會(huì)出現(xiàn)熱點(diǎn)key過期后產(chǎn)生的問題州藕,也就是“物理”不過期。

  • 從功能層面來看酝陈,為每個(gè)value設(shè)置一個(gè)邏輯過期時(shí)間,當(dāng)發(fā)現(xiàn)超過邏輯過期時(shí)間后毁涉,會(huì)使用單獨(dú)的線程去構(gòu)建緩存(相當(dāng)于不使用redis的過期功能沉帮,而是自己實(shí)現(xiàn)過期判斷邏輯)。

    示例代碼如下:

    String get(final String key){
        V v = redis.get(key);
        String value = v.getValue();
        long logicTimeout = v.getLogicTimeout();// 自定義邏輯超時(shí)時(shí)間
        if(logicTimeout<=System.currentTimeMillis()){
            String mutexKey = "mutex:key:"+key; // 設(shè)置作為上鎖的key
            if(redis.set(mutexKey,"1","ex 180","nx")){ // 使用setnx上鎖
                threadPool.execute(new Runnable(){
                    public void run(){
                        String dbValue = db.get(key); // 從數(shù)據(jù)庫中獲取
                        redis.set(key,(dbValue,newLogicTimeout)); // 重建該key的緩存
                        redis.delete(mutexKey); // 解鎖
                    }
                });
            }
        }
    }
    

    上述實(shí)現(xiàn)有一個(gè)問題就是在單獨(dú)創(chuàng)建線程重新構(gòu)建緩存的過程中贫堰,如果有其他服務(wù)去獲取緩存中的該值就會(huì)取到舊值穆壕,即出現(xiàn)短暫數(shù)據(jù)不一致的問題。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末其屏,一起剝皮案震驚了整個(gè)濱河市喇勋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌偎行,老刑警劉巖川背,帶你破解...
    沈念sama閱讀 211,743評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蛤袒,居然都是意外死亡熄云,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門妙真,熙熙樓的掌柜王于貴愁眉苦臉地迎上來缴允,“玉大人,你說我怎么就攤上這事珍德×钒悖” “怎么了?”我有些...
    開封第一講書人閱讀 157,285評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵锈候,是天一觀的道長薄料。 經(jīng)常有香客問我,道長晴及,這世上最難降的妖魔是什么都办? 我笑而不...
    開封第一講書人閱讀 56,485評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮虑稼,結(jié)果婚禮上琳钉,老公的妹妹穿的比我還像新娘。我一直安慰自己蛛倦,他們只是感情好歌懒,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,581評(píng)論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著溯壶,像睡著了一般及皂。 火紅的嫁衣襯著肌膚如雪甫男。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評(píng)論 1 290
  • 那天验烧,我揣著相機(jī)與錄音板驳,去河邊找鬼。 笑死碍拆,一個(gè)胖子當(dāng)著我的面吹牛若治,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播感混,決...
    沈念sama閱讀 38,960評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼端幼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了弧满?” 一聲冷哼從身側(cè)響起婆跑,我...
    開封第一講書人閱讀 37,719評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎庭呜,沒想到半個(gè)月后滑进,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,186評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡疟赊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,516評(píng)論 2 327
  • 正文 我和宋清朗相戀三年郊供,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片近哟。...
    茶點(diǎn)故事閱讀 38,650評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡驮审,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吉执,到底是詐尸還是另有隱情疯淫,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評(píng)論 4 330
  • 正文 年R本政府宣布戳玫,位于F島的核電站熙掺,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏咕宿。R本人自食惡果不足惜币绩,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,936評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望府阀。 院中可真熱鬧缆镣,春花似錦、人聲如沸试浙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽田巴。三九已至钠糊,卻和暖如春挟秤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背抄伍。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評(píng)論 1 266
  • 我被黑心中介騙來泰國打工艘刚, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人截珍。 一個(gè)月前我還...
    沈念sama閱讀 46,370評(píng)論 2 360
  • 正文 我出身青樓昔脯,卻偏偏與公主長得像,于是被迫代替她去往敵國和親笛臣。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,527評(píng)論 2 349

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

  • 2818.8.14 感恩父母給予生命隧饼,養(yǎng)育之恩不圖回報(bào) 感恩父母辛苦付出沈堡,這么老了還在為子女操心 感恩父母每次我回...
    wangqiaoyu閱讀 257評(píng)論 0 1
  • 大家都知道潘金蓮,乍一說起這個(gè)名字燕雁,首先想到的往往是水滸傳里那個(gè)風(fēng)流女子诞丽,往往招致大家的謾罵聲,這是一種普遍的看法...
    浪漫的高貴閱讀 341評(píng)論 0 7
  • 一、能賺捏浊,但是肯定不多懂衩。 經(jīng)常會(huì)看到年邁的老頭老太太在路邊擺攤,賣各種東西的都有金踪,自己做的鞋墊浊洞,地圖,指甲...
    日新木子閱讀 1,369評(píng)論 1 8
  • 昨日有幸聆聽了步綰老師在談寫作欄目“紙上電臺(tái)”微信群里面的語音分享胡岔,感觸頗多法希,現(xiàn)將自己的聽課筆記分享如下。 一靶瘸、對(duì)...
    知唐奶爸閱讀 773評(píng)論 8 14
  • 連綿的陰雨苫亦, 大海都被淹了吧, 那些剛開的花怨咪,也無可奈何 還好陽光在這個(gè)黃昏降臨屋剑, 可是天的臉色并不好看 晨光霧靄...
    天使在K歌閱讀 76評(píng)論 0 1