在生產(chǎn)環(huán)境中,會(huì)因?yàn)楹芏嗟脑蛟斐稍L問(wèn)請(qǐng)求繞過(guò)了緩存,都需要訪問(wèn)數(shù)據(jù)庫(kù)持久層,雖然對(duì)Redsi緩存服務(wù)器不會(huì)造成影響,但是數(shù)據(jù)庫(kù)的負(fù)載就會(huì)增大,使緩存的作用降低
一 緩存穿透
1,概念
??緩存穿透是指查詢一個(gè)根本不存在的數(shù)據(jù),緩存層和持久層都不會(huì)命中。在日常工作中出于容錯(cuò)的考慮,如果從持久層查不到數(shù)據(jù)則不寫(xiě)入緩存層,緩存穿透將導(dǎo)致不存在的數(shù)據(jù)每次請(qǐng)求都要到持久層去查詢,失去了緩存保護(hù)后端持久的意義。
緩存穿透示意圖:
??緩存穿透問(wèn)題可能會(huì)使后端存儲(chǔ)負(fù)載加大,由于很多后端持久層不具備高并發(fā)性,甚至可能造成后端存儲(chǔ)宕機(jī)。通常可以在程序中統(tǒng)計(jì)總調(diào)用數(shù)、緩存層命中數(shù)、如果同一個(gè)Key的緩存命中率很低,可能就是出現(xiàn)了緩存穿透問(wèn)題。
2,原因
- 自身業(yè)務(wù)代碼或者數(shù)據(jù)出現(xiàn)問(wèn)題(例如:set 和 get 的key不一致)
- 惡意攻擊,爬蟲(chóng)等造成大量空命中(爬取線上商城商品數(shù)據(jù),超大循環(huán)遞增商品的ID)
3,解決方案
1.緩存空對(duì)象
??緩存空對(duì)象:是指在持久層沒(méi)有命中的情況下,對(duì)key進(jìn)行set (key,null)。緩存空對(duì)象會(huì)有兩個(gè)問(wèn)題:
- value為null 不代表不占用內(nèi)存空間,空值做了緩存,意味著緩存層中存了更多的鍵,需要更多的內(nèi)存空間,比較有效的方法是針對(duì)這類數(shù)據(jù)設(shè)置一個(gè)較短的過(guò)期時(shí)間,讓其自動(dòng)剔除。
- 緩存層和存儲(chǔ)層的數(shù)據(jù)會(huì)有一段時(shí)間窗口的不一致,可能會(huì)對(duì)業(yè)務(wù)有一定影響。例如過(guò)期時(shí)間設(shè)置為5分鐘,如果此時(shí)存儲(chǔ)層添加了這個(gè)數(shù)據(jù),那此段時(shí)間就會(huì)出現(xiàn)緩存層和存儲(chǔ)層數(shù)據(jù)的不一致,此時(shí)可以利用消息系統(tǒng)或者其他方式清除掉緩存層中的空對(duì)象。
2.布隆過(guò)濾器
??在訪問(wèn)緩存層和存儲(chǔ)層之前,將存在的key用布隆過(guò)濾器提前保存起來(lái),做第一層攔截,當(dāng)收到一個(gè)對(duì)key請(qǐng)求時(shí)先用布隆過(guò)濾器驗(yàn)證是key否存在,如果存在在進(jìn)入緩存層、存儲(chǔ)層。可以使用bitmap做布隆過(guò)濾器。這種方法適用于數(shù)據(jù)命中不高、數(shù)據(jù)相對(duì)固定、實(shí)時(shí)性低的應(yīng)用場(chǎng)景,代碼維護(hù)較為復(fù)雜,但是緩存空間占用少。
??布隆過(guò)濾器實(shí)際上是一個(gè)很長(zhǎng)的二進(jìn)制數(shù)組和一系列隨機(jī)映射函數(shù)。布隆過(guò)濾器可以用于檢索一個(gè)元素是否在一個(gè)集合中。它的優(yōu)點(diǎn)是空間效率和查詢時(shí)間都遠(yuǎn)遠(yuǎn)超過(guò)一般的算法,缺點(diǎn)是有一定的誤識(shí)別率和刪除困難。
算法描述:
- 初始狀態(tài)時(shí),BloomFilter是一個(gè)長(zhǎng)度為m的位數(shù)組,每一位都置為0。
- 添加元素x時(shí),x使用k個(gè)hash函數(shù)得到k個(gè)hash值,對(duì)m取余,對(duì)應(yīng)的bit位設(shè)置為1。
- 判斷y是否屬于這個(gè)集合,對(duì)y使用k個(gè)哈希函數(shù)得到k個(gè)哈希值,對(duì)m取余,所有對(duì)應(yīng)的位置都是1,則認(rèn)為y屬于該集合(哈希沖突,可能存在誤判),否則就認(rèn)為y不屬于該集合??梢酝ㄟ^(guò)增加哈希函數(shù)和增加二進(jìn)制位數(shù)組的長(zhǎng)度來(lái)降低錯(cuò)報(bào)率。
錯(cuò)報(bào)原因:
??一個(gè)key映射數(shù)組上多位,一位會(huì)被多個(gè)key使用,也就是多對(duì)多的關(guān)系。如果一個(gè)key映射的所有位值為1,就判斷為存在。但是可能會(huì)出現(xiàn)key1 和 key2 同時(shí)映射到下標(biāo)為100的位,key1不存在,key2存在,這種情況下會(huì)發(fā)生錯(cuò)誤率
3,兩種方式對(duì)比
二 緩存雪崩
1,原因
- 由于緩存層承載著大量請(qǐng)求,有效地保護(hù)了存儲(chǔ)層,但是如果緩存層由于某些原因不可用(宕機(jī))
- 大量緩存由于超時(shí)時(shí)間相同在同一時(shí)間段失效(大批key失效/熱點(diǎn)數(shù)據(jù)失效),大量請(qǐng)求直接到達(dá)存儲(chǔ)層,存儲(chǔ)層壓力過(guò)大導(dǎo)致系統(tǒng)雪崩。
2,解決方案
- 可以把緩存層設(shè)計(jì)成高可用的,即使個(gè)別節(jié)點(diǎn)、個(gè)別機(jī)器、甚至是機(jī)房宕掉,依然可以提供服務(wù)。利用sentinel或cluster實(shí)現(xiàn)。
- 采用多級(jí)緩存:本地進(jìn)程作為一級(jí)緩存,redis作為二級(jí)緩存,不同級(jí)別的緩設(shè)置超時(shí)時(shí)間不同,即使某個(gè)緩存過(guò)期了,那么也有其他級(jí)別的緩存兜底。
- 緩存過(guò)期的時(shí)間盡量采用隨機(jī)性,盡量讓不同的key在不同的時(shí)間過(guò)期。例如:定時(shí)任務(wù)新建大批key的時(shí)候,設(shè)置不同的過(guò)期時(shí)間
三 緩存擊穿
1,原因
- 熱點(diǎn)key的并發(fā)量特別大
- 在緩存失效的瞬間會(huì)有大量的線程來(lái)重建緩存,但是緩存重建不能在短時(shí)間內(nèi)重建,造成后端負(fù)載過(guò)大從而讓?xiě)?yīng)用奔潰。
2,解決方案
1.分布式互斥鎖:就是在線程重建緩存的時(shí)候,設(shè)置一個(gè)分布式鎖,只讓一個(gè)線程重建緩存即可,其他線程讀取緩存數(shù)據(jù)即可。set key value [expiration EX seconds|PX milliseconds] [NX|XX]
2.永不過(guò)期
從緩存層面來(lái)看,確實(shí)沒(méi)有設(shè)置過(guò)期時(shí)間,所以不會(huì)出現(xiàn)熱點(diǎn)key過(guò)期后產(chǎn)生的問(wèn)題,也就是“物理”不過(guò)期。從功能層面來(lái)看,為每個(gè)value設(shè)置一個(gè)邏輯過(guò)期時(shí)間,當(dāng)發(fā)現(xiàn)超過(guò)邏輯過(guò)期時(shí)間后,會(huì)使用單獨(dú)的線程去更新緩
3,方案對(duì)比
2種方案對(duì)比
分布式互斥鎖: 這種方案思路比較簡(jiǎn)單,但是存在一定的隱患,如果在查詢數(shù)據(jù)庫(kù) + 和 重建緩存(key失效后進(jìn)行了大量的計(jì)算)時(shí)間過(guò)長(zhǎng),也可能會(huì)存在死鎖和線程池阻塞的風(fēng)險(xiǎn),高并發(fā)情景下吞吐量會(huì)大大降低!但是這種方法能夠較好地降低后端存儲(chǔ)負(fù)載,并在一致性上做得比較好。
“永遠(yuǎn)不過(guò)期”: 這種方案由于沒(méi)有設(shè)置真正的過(guò)期時(shí)間,實(shí)際上已經(jīng)不存在熱點(diǎn)key產(chǎn)生的一系列危害,但是會(huì)存在數(shù)據(jù)不一致的情況,同時(shí)代碼復(fù)雜度會(huì)增大。
四 其他相關(guān)問(wèn)題
1,熱點(diǎn)key
當(dāng)有大量的請(qǐng)求(幾十萬(wàn))訪問(wèn)某個(gè)Redis某個(gè)key時(shí),由于流量集中達(dá)到網(wǎng)絡(luò)上限,從而導(dǎo)致這個(gè)redis的服務(wù)器宕機(jī)。造成緩存擊穿,接下來(lái)對(duì)這個(gè)key的訪問(wèn)將直接訪問(wèn)數(shù)據(jù)庫(kù)造成數(shù)據(jù)庫(kù)崩潰,或者訪問(wèn)數(shù)據(jù)庫(kù)回填Redis再訪問(wèn)Redis,繼續(xù)崩潰。
1.發(fā)現(xiàn)熱點(diǎn)key
- 預(yù)估熱key,比如秒殺的商品、火爆的新聞等
- 在客戶端進(jìn)行統(tǒng)計(jì),實(shí)現(xiàn)簡(jiǎn)單,加一行代碼即可
- 如果是Proxy,比如Codis,可以在Proxy端收集
- 利用Redis自帶的命令,monitor、hotkeys。但是執(zhí)行緩慢(不要用)
- 利用基于大數(shù)據(jù)領(lǐng)域的流式計(jì)算技術(shù)來(lái)進(jìn)行實(shí)時(shí)數(shù)據(jù)訪問(wèn)次數(shù)的統(tǒng)計(jì),比如 Storm、Spark、Streaming、Flink,這些技術(shù)都是可以的。發(fā)現(xiàn)熱點(diǎn)數(shù)據(jù)后可以寫(xiě)到zookeeper中
2.解決方案
- 變分布式緩存為本地緩存,發(fā)現(xiàn)熱key后,把緩存數(shù)據(jù)取出后,直接加載到本地緩存中??梢圆捎肊hcache、Guava Cache都可以,這樣系統(tǒng)在訪問(wèn)熱key數(shù)據(jù)時(shí)就可以直接訪問(wèn)自己的緩存了。(數(shù)據(jù)不要求時(shí)時(shí)一致)
- 在每個(gè)Redis主節(jié)點(diǎn)上備份熱key數(shù)據(jù),這樣在讀取時(shí)可以采用隨機(jī)讀取的方式,將訪問(wèn)壓力負(fù)載到每個(gè)Redis上。
- 利用對(duì)熱點(diǎn)數(shù)據(jù)訪問(wèn)的限流熔斷保護(hù)措施,每個(gè)系統(tǒng)實(shí)例每秒最多請(qǐng)求緩存集群讀操作不超過(guò) 400 次,一超過(guò)就可以熔斷掉,不讓請(qǐng)求緩存集群,直接返回一個(gè)空白信息,然后用戶稍后會(huì)自行再次重新刷新頁(yè)面之類的。(首頁(yè)不行,系統(tǒng)友好性差)通過(guò)系統(tǒng)層自己直接加限流熔斷保護(hù)措施,可以很好的保護(hù)后面的緩存集群.
2,大key
大key指的是存儲(chǔ)的值(Value)非常大。大key會(huì)大量占用內(nèi)存,在集群中無(wú)法均衡,Redis的性能下降,主從復(fù)制異常。在主動(dòng)刪除或過(guò)期刪除時(shí)會(huì)操作時(shí)間過(guò)長(zhǎng)而引起服務(wù)阻塞
1,如何發(fā)現(xiàn)大key
- redis-cli --bigkeys命令??梢哉业侥硞€(gè)實(shí)例5種數(shù)據(jù)類型(String、hash、list、set、zset)的最大key。但如果Redis 的key比較多,執(zhí)行該命令會(huì)比較慢。
- 獲取生產(chǎn)Redis的rdb文件,通過(guò)rdbtools分析rdb生成csv文件,再導(dǎo)入MySQL或其他數(shù)據(jù)庫(kù)中進(jìn)行分析統(tǒng)計(jì),根據(jù)size_in_bytes統(tǒng)計(jì)big key
2,解決方案
- string類型的big key,盡量不要存入Redis中,可以使用文檔型數(shù)據(jù)庫(kù)MongoDB或緩存到CDN上。如果必須用Redis存儲(chǔ),最好單獨(dú)存儲(chǔ),不要和其他key一起存儲(chǔ)。采用一主一從或多從。
- 單個(gè)簡(jiǎn)單key存儲(chǔ)的value很大,可以嘗試將對(duì)象分拆成幾個(gè)key-value, 使用mget獲取值,這樣分拆的意義在于分拆單次操作的壓力,將操作壓力平攤到多次操作中,降低對(duì)redis的IO影響。hash, set,zset,list 中存儲(chǔ)過(guò)多的元素,可以將這些元素分拆。
以hash類型舉例來(lái)說(shuō),對(duì)于field過(guò)多的場(chǎng)景,可以根據(jù)field進(jìn)行hash取模,生成一個(gè)新的key,例如原來(lái)的
hash_key:{filed1:value, filed2:value, filed3:value …},可以hash取模后形成如下
key:value形式
hash_key:1:{filed1:value}
hash_key:2:{filed2:value}
hash_key:3:{filed3:value}
…
取模后,將原先單個(gè)key分成多個(gè)key,每個(gè)key filed個(gè)數(shù)為原先的1/N文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-408319.html
- 刪除大key時(shí)不要使用del,因?yàn)閐el是阻塞命令,刪除時(shí)會(huì)影響性能。使用 **lazy delete (unlink命令)**刪除指定的key(s),若key不存在則該key被跳過(guò)。但是,相比DEL會(huì)產(chǎn)生阻塞,該命令會(huì)在另一個(gè)線程中回收內(nèi)存,因此它是非阻塞的。 這也是該命令名字的由來(lái):僅將keys從key空間中刪除,真正的數(shù)據(jù)刪除會(huì)在后續(xù)異步操作。
參考鏈接:
https://blog.csdn.net/womenyiqilalala/article/details/105205532文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-408319.html
到了這里,關(guān)于Redis之緩存穿透+緩存雪崩+緩存擊穿的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!