Redis企業(yè)級的緩存設計,緩存穿透吠各,緩存擊穿原理解釋

緩存的收益與成本

①收益

  • 加速讀寫:因為緩存通常都是全內(nèi)存的(例如Redis、Memcache)勉抓,而存儲層通常讀寫性能不夠強悍(例如MySQL)贾漏,通過緩存的使用可以有效地加速讀寫,優(yōu)化用戶體驗藕筋。
  • 降低后端負載:幫助后端減少訪問量和復雜計算(例如很復雜的SQL語句)纵散,在很大程度降低了后端的負載。
    ②成本
  • 數(shù)據(jù)不一致性:緩存層和存儲層的數(shù)據(jù)存在著一定時間窗口的不一致性隐圾,時間窗口跟更新策略有關(guān)伍掀。
  • 代碼維護成本:加入緩存后,需要同時處理緩存層和存儲層的邏輯暇藏,增大了開發(fā)者維護代碼的成本蜜笤。
  • 運維成本:以Redis Cluster為例,加入后無形中增加了運維成本盐碱。
    使用場景
  • 開銷大的復雜計算:以MySQL為例子把兔,一些復雜的操作或者計算(例如大量聯(lián)表操作沪伙、一些分組計算),如果不加緩存县好,不但無法滿足高并發(fā)量围橡,同時也會給MySQL帶來巨大的負擔。
  • 加速請求響應:即使查詢單條后端數(shù)據(jù)足夠快缕贡,那么依然可以使用緩存翁授,以Redis為例子,每秒可以完成數(shù)萬次讀寫晾咪,并且提供的批量操作可以優(yōu)化整個IO鏈的響應時間

緩存更新策略

內(nèi)存溢出淘汰策略

當Redis所用內(nèi)存達到maxmemory上限(used_memory>maxmemory)時會觸發(fā)相應的溢出控制策略收擦。具體策略受maxmemory-policy參數(shù)控制。
Redis支持6種策略:
1)noeviction:默認策略禀酱,不會刪除任何數(shù)據(jù)炬守,拒絕所有寫入操作并返回客戶端錯誤信息(error)OOM command not allowed when used memory,此時Redis只響應讀操作剂跟。
2)volatile-lru:根據(jù)LRU算法刪除設置了超時屬性(expire)的鍵减途,直到騰出足夠空間為止。如果沒有可刪除的鍵對象曹洽,回退到noeviction策略鳍置。
3)volatile-random:隨機刪除過期鍵,直到騰出足夠空間為止送淆。
4)allkeys-lru:根據(jù)LRU算法刪除鍵税产,不管數(shù)據(jù)有沒有設置超時屬性,直到騰出足夠空間為止偷崩。
5)allkeys-random:隨機刪除所有鍵辟拷,直到騰出足夠空間為止。
6)volatile-ttl:根據(jù)鍵值對象的ttl屬性阐斜,刪除最近將要過期數(shù)據(jù)衫冻。如果沒有,回退到noeviction策略


內(nèi)存溢出控制策略可以采用config set maxmemory-policy{policy}動態(tài)配置谒出。
寫命令導致當內(nèi)存溢出時會頻繁執(zhí)行回收內(nèi)存成本很高隅俘,如果Redis有從節(jié)點,回收內(nèi)存操作對應的刪除命令會同步到從節(jié)點笤喳,導致寫放大的問題为居。


過期刪除
  • 惰性刪除
    Redis的每個庫都有一個過期字典,過期字典中保存所有key的過期時間杀狡。當客戶端讀取一個key時會先到過期字典內(nèi)查詢key是否已經(jīng)過期蒙畴,如果已經(jīng)超過鍵,會執(zhí)行刪除操作并返回空呜象。忍抽,這種策略是出于節(jié)省CPU成本考慮八孝,但是單獨用這種方式存在內(nèi)存泄露的問題,當過期鍵一直沒有訪問將無法得到及時刪除鸠项,從而導致內(nèi)存不能及時釋放干跛。
  • 定時刪除
    Redis內(nèi)部維護一個定時任務,默認每秒運行10次祟绊。通過hz修改運行次數(shù)楼入。定時任務中刪除過期鍵邏輯采用了自適應算法,根據(jù)鍵的過期比例牧抽、使用快慢兩種速率模式回收鍵嘉熊。ServerCron
    慢模式:定時任務執(zhí)行時間超過25毫秒自動退出
    快模式:上次執(zhí)行時間超過25毫秒,則采用快模式扬舒,快模式下超時時間為1毫秒且2秒內(nèi)只能運行1次阐肤。


應用方更新

a、應用程序先從cache取數(shù)據(jù)讲坎,沒有得到孕惜,則從數(shù)據(jù)庫中取數(shù)據(jù),成功后晨炕,放到緩存中衫画。



b、先刪除緩存瓮栗,再更新數(shù)據(jù)庫
這個操作有一個比較大的問題削罩,更新數(shù)據(jù)的請求在對緩存刪除完之后,又收到一個讀請求费奸,這個時候由于緩存被刪除所以直接會讀庫弥激,讀操作的數(shù)據(jù)是老的并且會被加載進入緩存當中,后續(xù)讀請求全部訪問的老數(shù)據(jù)愿阐。
c微服、先更新數(shù)據(jù)庫,再刪除緩存(推薦)
為什么不是寫完數(shù)據(jù)庫后更新緩存换况?主要是怕兩個并發(fā)的寫操作導致臟數(shù)據(jù)。

緩存粒度

通用性
緩存全部數(shù)據(jù)比部分數(shù)據(jù)更加通用盗蟆,但從實際經(jīng)驗看戈二,很長時間內(nèi)應用只需要幾個重要的屬性。
占用空間
緩存全部數(shù)據(jù)要比部分數(shù)據(jù)占用更多的空間喳资,存在以下問題:

  • 全部數(shù)據(jù)會造成內(nèi)存的浪費觉吭。
  • 全部數(shù)據(jù)可能每次傳輸產(chǎn)生的網(wǎng)絡流量會比較大,耗時相對較大仆邓,在極端情況下會阻塞網(wǎng)絡鲜滩。
  • 全部數(shù)據(jù)的序列化和反序列化的CPU開銷更大伴鳖。
    代碼維護
    全部數(shù)據(jù)的優(yōu)勢更加明顯,而部分數(shù)據(jù)一旦要加新字段需要修改業(yè)務代碼徙硅,而且修改后通常還需要刷新緩存數(shù)據(jù)榜聂。

緩存穿透

緩存穿透是指查詢一個根本不存在的數(shù)據(jù),緩存層和持久層都不會命中嗓蘑,通常出于容錯的考慮须肆,如果從持久層查不到數(shù)據(jù)則不寫入緩存層。

緩存穿透示意圖

緩存穿透將導致不存在的數(shù)據(jù)每次請求都要到持久層去查詢桩皿,失去了緩存保護后端持久的意義豌汇。
緩存穿透問題可能會使后端存儲負載加大,由于很多后端持久層不具備高并發(fā)性泄隔,甚至可能造成后端存儲宕掉拒贱。通常可以在程序中統(tǒng)計總調(diào)用數(shù)佛嬉、緩存層命中數(shù)逻澳、如果同一個Key的緩存命中率很低,可能就是出現(xiàn)了緩存穿透問題巷燥。
造成緩存穿透的基本原因有兩個赡盘。第一,自身業(yè)務代碼或者數(shù)據(jù)出現(xiàn)問題缰揪,第二陨享,一些惡意攻擊、爬蟲等造成大量空命中钝腺。
①緩存空對象

緩存空對象會有兩個問題:第一抛姑,空值做了緩存,意味著緩存層中存了更多的鍵艳狐,需要更多的內(nèi)存空間定硝,比較有效的方法是針對這類數(shù)據(jù)設置一個較短的過期時間,讓其自動剔除毫目。第二蔬啡,緩存層和存儲層的數(shù)據(jù)會有一段時間窗口的不一致,可能會對業(yè)務有一定影響镀虐。例如過期時間設置為5分鐘箱蟆,如果此時存儲層添加了這個數(shù)據(jù),那此段時間就會出現(xiàn)緩存層和存儲層數(shù)據(jù)的不一致刮便,此時可以利用消息系統(tǒng)或者其他方式清除掉緩存層中的空對象空猜。
②布隆過濾器攔截
在訪問緩存層和存儲層之前,將存在的key用布隆過濾器提前保存起來,做第一層攔截辈毯,當收到一個對key請求時先用布隆過濾器驗證是key否存在坝疼,如果存在在進入緩存層、存儲層谆沃《坌祝可以使用bitmap做布隆過濾器。這種方法適用于數(shù)據(jù)命中不高管毙、數(shù)據(jù)相對固定腿椎、實時性低的應用場景,代碼維護較為復雜夭咬,但是緩存空間占用少啃炸。
布隆過濾器實際上是一個很長的二進制向量和一系列隨機映射函數(shù)。布隆過濾器可以用于檢索一個元素是否在一個集合中卓舵。它的優(yōu)點是空間效率和查詢時間都遠遠超過一般的算法南用,缺點是有一定的誤識別率和刪除困難。
算法描述:

  • 初始狀態(tài)時掏湾,BloomFilter是一個長度為m的位數(shù)組裹虫,每一位都置為0。
  • 添加元素x時融击,x使用k個hash函數(shù)得到k個hash值筑公,對m取余,對應的bit位設置為1尊浪。
  • 判斷y是否屬于這個集合匣屡,對y使用k個哈希函數(shù)得到k個哈希值,對m取余拇涤,所有對應的位置都是1捣作,則認為y屬于該集合(哈希沖突,可能存在誤判)鹅士,否則就認為y不屬于該集合券躁。可以通過增加哈希函數(shù)和增加二進制位數(shù)組的長度來降低誤報率掉盅。

    方案對比:

緩存雪崩

由于緩存層承載著大量請求也拜,有效地保護了存儲層,但是如果緩存層由于某些原因
不可用或者大量緩存由于超時時間相同在同一時間段失效趾痘,大量請求直接到達存儲層慢哈,存儲層壓力過大導致系統(tǒng)雪崩。


解決方案:

  • 可以把緩存層設計成高可用的扼脐,即使個別節(jié)點岸军、個別機器、甚至是機房宕掉瓦侮,依然可以提供服務艰赞。利用sentinel或cluster實現(xiàn)。
  • 采用多級緩存肚吏,本地進程作為一級緩存方妖,redis作為二級緩存,不同級別的緩存設置的超時時間不同罚攀,即使某級緩存過期了党觅,也有其他級別緩存兜底。
  • 緩存的過期時間用隨機值斋泄,盡量讓不同的key的過期時間不同杯瞻。

緩存擊穿

系統(tǒng)中存在以下兩個問題時需要引起注意:

  • 當前key是一個熱點key(例如一個秒殺活動),并發(fā)量非常大炫掐。
    重建緩存不能在短時間完成魁莉,可能是一個復雜計算,例如復雜的SQL募胃、多次IO旗唁、多個依賴等。
  • 在緩存失效的瞬間痹束,有大量線程來重建緩存检疫,造成后端負載加大,甚至可能會讓應用崩潰祷嘶。
    解決方案:
    ①分布式互斥鎖
    只允許一個線程重建緩存屎媳,其他線程等待重建緩存的線程執(zhí)行完,重新從緩存獲取數(shù)據(jù)即可抹蚀。set(key,value,timeout)

    ②永不過期
  • 從緩存層面來看剿牺,確實沒有設置過期時間,所以不會出現(xiàn)熱點key過期后產(chǎn)生的問題环壤,也就是“物理”不過期晒来。
  • 從功能層面來看,為每個value設置一個邏輯過期時間郑现,當發(fā)現(xiàn)超過邏輯過期時間后湃崩,會使用單獨的線程去更新緩存。

    2種方案對比:
  • 分布式互斥鎖:這種方案思路比較簡單接箫,但是存在一定的隱患攒读,如果構(gòu)建緩存過程出現(xiàn)問題或者時間較長,可能會存在死鎖和線程池阻塞的風險辛友,但是這種方法能夠較好地降低后端存儲負載薄扁,并在一致性上做得比較好剪返。
  • “永遠不過期”:這種方案由于沒有設置真正的過期時間,實際上已經(jīng)不存在熱點key產(chǎn)生的一系列危害邓梅,但是會存在數(shù)據(jù)不一致的情況脱盲,同時代碼復雜度會增大。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末日缨,一起剝皮案震驚了整個濱河市钱反,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌匣距,老刑警劉巖面哥,帶你破解...
    沈念sama閱讀 217,826評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異毅待,居然都是意外死亡尚卫,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,968評論 3 395
  • 文/潘曉璐 我一進店門尸红,熙熙樓的掌柜王于貴愁眉苦臉地迎上來焕毫,“玉大人,你說我怎么就攤上這事驶乾∫仂” “怎么了?”我有些...
    開封第一講書人閱讀 164,234評論 0 354
  • 文/不壞的土叔 我叫張陵级乐,是天一觀的道長疙咸。 經(jīng)常有香客問我,道長风科,這世上最難降的妖魔是什么撒轮? 我笑而不...
    開封第一講書人閱讀 58,562評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮贼穆,結(jié)果婚禮上题山,老公的妹妹穿的比我還像新娘。我一直安慰自己故痊,他們只是感情好顶瞳,可當我...
    茶點故事閱讀 67,611評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著愕秫,像睡著了一般慨菱。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上戴甩,一...
    開封第一講書人閱讀 51,482評論 1 302
  • 那天符喝,我揣著相機與錄音,去河邊找鬼甜孤。 笑死协饲,一個胖子當著我的面吹牛畏腕,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播茉稠,決...
    沈念sama閱讀 40,271評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼郊尝,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了战惊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,166評論 0 276
  • 序言:老撾萬榮一對情侶失蹤扎即,失蹤者是張志新(化名)和其女友劉穎吞获,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體谚鄙,經(jīng)...
    沈念sama閱讀 45,608評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡各拷,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,814評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了闷营。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片烤黍。...
    茶點故事閱讀 39,926評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖傻盟,靈堂內(nèi)的尸體忽然破棺而出速蕊,到底是詐尸還是另有隱情,我是刑警寧澤娘赴,帶...
    沈念sama閱讀 35,644評論 5 346
  • 正文 年R本政府宣布规哲,位于F島的核電站,受9級特大地震影響诽表,放射性物質(zhì)發(fā)生泄漏唉锌。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,249評論 3 329
  • 文/蒙蒙 一竿奏、第九天 我趴在偏房一處隱蔽的房頂上張望袄简。 院中可真熱鬧,春花似錦泛啸、人聲如沸绿语。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,866評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽汞舱。三九已至,卻和暖如春宗雇,著一層夾襖步出監(jiān)牢的瞬間昂芜,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,991評論 1 269
  • 我被黑心中介騙來泰國打工赔蒲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留泌神,地道東北人毒费。 一個月前我還...
    沈念sama閱讀 48,063評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像喜爷,于是被迫代替她去往敵國和親耍属。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 44,871評論 2 354

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

  • 本文损趋,你將閱讀到以下內(nèi)容: 如何應對緩存擊穿和緩存雪崩的問題患久; Redis 的過期策略以及內(nèi)存淘汰機制; 1.如何...
    Terminalist閱讀 480評論 0 1
  • 緩存架構(gòu) 腦中的直觀反應 SQLAlchemy起到一定的本地緩存作用在同一請求中多次相同的查詢只查詢數(shù)據(jù)庫一次浑槽,S...
    大金葉子閱讀 2,928評論 0 2
  • 一蒋失、Redis 1、概述 Redis是速度非惩┎#快的非關(guān)系型內(nèi)存鍵值數(shù)據(jù)庫篙挽,可以存儲鍵和物種不同類型的值之間的映射。...
    落地生涯閱讀 781評論 0 3
  • 1.什么是redis? Redis 是一個基于內(nèi)存的高性能key-value數(shù)據(jù)庫镊靴。 2.Reids的特點 Red...
    java成功之路閱讀 425評論 0 8
  • 《于丹<論語>心得》于昨日讀完铣卡。這本書我購置已久,卻苦無時間閱讀偏竟,束之高閣煮落。近段時間,偶然間翻閱了一下踊谋,萌生了讀完...
    孔玲外小閱讀 156評論 2 3