title: 緩存三大問題解決方案
date: 2021/05/25 10:57
緩存穿透
緩存穿透指的是查詢一個(gè)一定不存在的數(shù)據(jù)期揪,由于存儲(chǔ)層在查不到數(shù)據(jù)時(shí)不寫入緩存掉奄,這將導(dǎo)致這個(gè)不存在的數(shù)據(jù)每次請(qǐng)求都要到存儲(chǔ)層查詢,從而失去了緩存的意義凤薛。
如果有人利用不存在的key頻繁攻擊我們的應(yīng)用姓建,可能 DB 就掛掉了诞仓。
解決方案
- 布隆過濾器,將所有可能存在的數(shù)據(jù) hash 到一個(gè)足夠大的 bitmap 中速兔,一個(gè)一定不存在的數(shù)據(jù)則會(huì)被攔截掉墅拭,從而避免了對(duì)底層存儲(chǔ)系統(tǒng)的查詢壓力。
因?yàn)?BloomFilter 如果聲稱其中包含元素憨栽,則可能是錯(cuò)誤的帜矾,但是如果聲明其中不包含元素,則肯定是正確的屑柔。
- 空值緩存:如果一個(gè)查詢返回的數(shù)據(jù)為空(不管是數(shù)據(jù)不存在屡萤,還是系統(tǒng)故障),我們?nèi)匀话堰@個(gè)空結(jié)果進(jìn)行緩存掸宛,但它的過期時(shí)間會(huì)很短死陆,最長不超過五分鐘,這樣則可以應(yīng)對(duì)短時(shí)間的大量的該key攻擊唧瘾,設(shè)置為較短的失效時(shí)間是因?yàn)樵撝悼赡軜I(yè)務(wù)無關(guān)措译,存在意義不大,且該次的查詢也未必是攻擊者發(fā)起饰序,無過久存儲(chǔ)的必要领虹,故可以早點(diǎn)失效。
緩存雪崩
緩存雪崩是指在我們設(shè)置緩存時(shí)采用了相同的過期時(shí)間求豫,導(dǎo)致緩存在某一時(shí)刻同時(shí)失效塌衰,請(qǐng)求全部轉(zhuǎn)發(fā)到DB,DB瞬時(shí)壓力過重雪崩蝠嘉。
另一種情況是緩存服務(wù)器宕機(jī)最疆,也會(huì)造成雪崩現(xiàn)象。
解決方案
永不過期:簡單粗暴
線程互斥:通過分布式鎖只允許一個(gè)線程構(gòu)建緩存蚤告,其他線程等待構(gòu)建緩存的線程執(zhí)行完努酸,重新從緩存獲取數(shù)據(jù)才可以,每個(gè)時(shí)刻只有一個(gè)線程在執(zhí)行請(qǐng)求杜恰,減輕了db的壓力获诈,但缺點(diǎn)也很明顯,降低了系統(tǒng)的qps箫章。
交錯(cuò)失效時(shí)間:這種方法時(shí)間比較簡單粗暴烙荷,既然在同一時(shí)間失效會(huì)造成請(qǐng)求過多雪崩,那我們錯(cuò)開不同的失效時(shí)間即可從一定長度上避免這種問題檬寂,在緩存進(jìn)行失效時(shí)間設(shè)置的時(shí)候终抽,從某個(gè)適當(dāng)?shù)闹涤蛑须S機(jī)一個(gè)時(shí)間作為失效時(shí)間即可。
-
請(qǐng)求限流:
- 限制請(qǐng)求數(shù)量,常見的限流算法有滑動(dòng)窗口昼伴,令牌桶算法和漏桶算法匾旭,或者直接使用隊(duì)列、加鎖(信號(hào)量)等
- 限制數(shù)據(jù)庫的每秒請(qǐng)求數(shù)圃郊,避免數(shù)據(jù)庫掛掉价涝。對(duì)于被限流的請(qǐng)求,采用服務(wù)降級(jí)處理持舆,比如提供默認(rèn)的值色瘩,或者空白值
本地緩存:如果使用本地緩存,即使分布式緩存掛了逸寓,也可以將數(shù)據(jù)庫查詢的結(jié)果緩存到本地居兆,避免后續(xù)請(qǐng)求全部達(dá)到數(shù)據(jù)庫中。
緩存高可用:使用Redis Sentinel等搭建緩存的高可用竹伸,避免緩存掛掉無法提供服務(wù)的情況泥栖,從而降低出現(xiàn)緩存雪崩的情況
緩存擊穿
緩存擊穿實(shí)際上是緩存雪崩的一個(gè)特例,對(duì)于一些設(shè)置了過期時(shí)間的key勋篓,如果這些key可能會(huì)在某些時(shí)間點(diǎn)被超高并發(fā)地訪問吧享,由于系統(tǒng)中對(duì)這些熱點(diǎn)的數(shù)據(jù)緩存也存在失效時(shí)間,在熱點(diǎn)的緩存到達(dá)失效時(shí)間時(shí)譬嚣,此時(shí)可能依然會(huì)有大量的請(qǐng)求到達(dá)系統(tǒng)钢颂,沒有了緩存層的保護(hù),這些請(qǐng)求同樣的會(huì)到達(dá)db從而可能引起故障拜银。擊穿與雪崩的區(qū)別即在于擊穿是對(duì)于特定的熱點(diǎn)數(shù)據(jù)來說甸陌,而雪崩是全部數(shù)據(jù)。
解決方案
- 永不過期:簡單粗暴
- 請(qǐng)求限流:
- 限制請(qǐng)求數(shù)量盐股,常見的限流算法有滑動(dòng)窗口,令牌桶算法和漏桶算法耻卡,或者直接使用隊(duì)列疯汁、加鎖(信號(hào)量)等
- 限制數(shù)據(jù)庫的每秒請(qǐng)求數(shù),避免數(shù)據(jù)庫掛掉卵酪。對(duì)于被限流的請(qǐng)求幌蚊,采用服務(wù)降級(jí)處理,比如提供默認(rèn)的值溃卡,或者空白值
- 本地緩存:使用 LRU 做二級(jí)緩存
擴(kuò)展 - 服務(wù)雪崩
在微服務(wù)架構(gòu)中溢豆,我們將業(yè)務(wù)拆分成一個(gè)個(gè)的服務(wù),服務(wù)與服務(wù)之間可以相互調(diào)用瘸羡,但是由于網(wǎng)絡(luò)原因或者自身的原因漩仙,服務(wù)并不能保證服務(wù)的100%可用,如果單個(gè)服務(wù)出現(xiàn)問題,調(diào)用這個(gè)服務(wù)就會(huì)出現(xiàn)網(wǎng)絡(luò)延遲队他,此時(shí)若有大量的網(wǎng)絡(luò)涌入卷仑,會(huì)形成任務(wù)堆積,最終導(dǎo)致服務(wù)癱瘓麸折。
由于服務(wù)與服務(wù)之間的依賴性锡凝,故障會(huì)傳播,會(huì)對(duì)整個(gè)微服務(wù)系統(tǒng)造成災(zāi)難性的嚴(yán)重后果垢啼,這就是服務(wù)故障的“雪崩效應(yīng)” 窜锯。
雪崩發(fā)生的原因多種多樣,有不合理的容量設(shè)計(jì)芭析,或者是高并發(fā)下某一個(gè)方法響應(yīng)變慢锚扎,亦或是某臺(tái)機(jī)器的資源耗盡。我們無法完全杜絕雪崩源頭的發(fā)生放刨,只有做好足夠的容錯(cuò)工秩,保證在一個(gè)服務(wù)發(fā)生問題,不會(huì)影響到其它服務(wù)的正常運(yùn)行进统。也就是"雪落而不雪崩"助币。
5.2 常見的容錯(cuò)方案
容錯(cuò)說白了就是保護(hù)自己不被豬隊(duì)友拖垮的一些措施,下面介紹常見的服務(wù)容錯(cuò)思路和組件螟碎。
常見的容錯(cuò)思路
1眉菱、隔離
它是指將系統(tǒng)按照一定的原則劃分為若干個(gè)服務(wù)模塊,各個(gè)模塊之間相對(duì)獨(dú)立掉分,無強(qiáng)依賴俭缓。當(dāng)有故障發(fā)生時(shí),能將問題和影響隔離在某個(gè)模塊內(nèi)部酥郭,而不擴(kuò)散風(fēng)險(xiǎn)华坦,不波及其它模塊,不影響整體的系統(tǒng)服務(wù)不从。常見的隔離方式有:線程池隔離和信號(hào)量隔離惜姐。
2、超時(shí)
在上游服務(wù)調(diào)用下游服務(wù)的時(shí)候椿息,設(shè)置一個(gè)最大響應(yīng)時(shí)間歹袁,如果超過這個(gè)時(shí)間,下游未作出反應(yīng)寝优,就斷開請(qǐng)求条舔,釋放掉線程。
3乏矾、限流
限流就是限制系統(tǒng)的輸入和輸出流量已達(dá)到保護(hù)系統(tǒng)的目的孟抗。為了保證系統(tǒng)的穩(wěn)固運(yùn)行迁杨,一旦達(dá)到的需要限制的閾值,就需要限制流量并采取少量措施以完成限制流量的目的夸浅。
4仑最、熔斷
在互聯(lián)網(wǎng)系統(tǒng)中,當(dāng)下游服務(wù)因訪問壓力過大而響應(yīng)變慢或失敗帆喇,上游服務(wù)為了保護(hù)系統(tǒng)整 體的可用性警医,可以暫時(shí)切斷對(duì)下游服務(wù)的調(diào)用。這種犧牲局部坯钦,保全整體的措施就叫做熔斷预皇。
服務(wù)熔斷一般有三種狀態(tài):
- 熔斷關(guān)閉狀態(tài)(Closed) :服務(wù)沒有故障時(shí),熔斷器所處的狀態(tài)婉刀,對(duì)調(diào)用方的調(diào)用不做任何限制
- 熔斷開啟狀態(tài)(Open) :后續(xù)對(duì)該服務(wù)接口的調(diào)用不再經(jīng)過網(wǎng)絡(luò)吟温,直接執(zhí)行本地的fallback方法
- 半熔斷狀態(tài)(Half-Open):嘗試恢復(fù)服務(wù)調(diào)用,允許有限的流量調(diào)用該服務(wù)突颊,并監(jiān)控調(diào)用成功率鲁豪。如果成功率達(dá)到預(yù)期,則說明服務(wù)已恢復(fù)律秃,進(jìn)入熔斷關(guān)閉狀態(tài)爬橡;如果成功率仍舊很低,則重新進(jìn)入熔斷關(guān)閉狀態(tài)棒动。
5糙申、降級(jí)
降級(jí)其實(shí)就是為服務(wù)提供一個(gè)托底方案,一旦服務(wù)無法正常調(diào)用船惨,就使用托底方案柜裸。
常見的容錯(cuò)組件
Hystrix:
Hystrix是由Netflix開源的一個(gè)延遲和容錯(cuò)庫,用于隔離訪問遠(yuǎn)程系統(tǒng)粱锐、服務(wù)或者第三方庫疙挺,防止級(jí)聯(lián)失敗,從而提升系統(tǒng)的可用性與容錯(cuò)性怜浅。
Resilience4J:
Resilicence4J一款非常輕量衔统、簡單,并且文檔非常清晰海雪、豐富的熔斷工具,這也是Hystrix官方推薦的替代產(chǎn)品舱殿。不僅如此奥裸,Resilicence4j還原生支持Spring Boot 1.x/2.x,而且監(jiān)控也支持和 prometheus等多款主流產(chǎn)品進(jìn)行整合沪袭。
Sentinel:
Sentinel 是阿里巴巴開源的一款斷路器實(shí)現(xiàn)湾宙,本身在阿里內(nèi)部已經(jīng)被大規(guī)模采用樟氢,非常穩(wěn)定。
Sentinel | Hystrix | resilience4j | |
---|---|---|---|
隔離策略 | 信號(hào)量隔離(并發(fā)線程數(shù)限流) | 線程池隔離/信號(hào)量隔離 | 信號(hào)量隔離 |
熔斷降級(jí)策略 | 基于響應(yīng)時(shí)間侠鳄、異常比率埠啃、異常數(shù)等 | 異常比率模式、超時(shí)熔斷 | 基于異常比率伟恶、響應(yīng)時(shí)間 |
實(shí)時(shí)統(tǒng)計(jì)實(shí)現(xiàn) | 滑動(dòng)窗口(LeapArray) | 滑動(dòng)窗口(基于 RxJava) | Ring Bit Buffer |
動(dòng)態(tài)規(guī)則配置 | 支持多種配置源 | 支持多種數(shù)據(jù)源 | 有限支持 |
擴(kuò)展性 | 豐富的 SPI 擴(kuò)展接口 | 插件的形式 | 接口的形式 |
基于注解的支持 | 支持 | 支持 | 支持 |
限流 | 基于 QPS碴开,支持基于調(diào)用關(guān)系的限流 | 有限的支持 | Rate Limiter |
集群流量控制 | 支持 | 不支持 | 不支持 |
流量整形 | 支持預(yù)熱模式、勻速排隊(duì)模式等多種復(fù)雜場(chǎng)景 | 不支持 | 簡單的 Rate Limiter 模式 |
系統(tǒng)自適應(yīng)保護(hù) | 支持 | 不支持 | 不支持 |
控制臺(tái) | 提供開箱即用的控制臺(tái)博秫,可配置規(guī)則潦牛、查看秒級(jí)監(jiān)控、機(jī)器發(fā)現(xiàn)等 | 簡單的監(jiān)控查看 | 不提供控制臺(tái)挡育,可對(duì)接其它監(jiān)控系統(tǒng) |
多語言支持 | Java / C++ | Java | Java |
開源社區(qū)狀態(tài) | 活躍 | 停止維護(hù) | 較活躍 |