緩存使用過(guò)程中剩岳,我們經(jīng)常遇到的問(wèn)題有一下四個(gè):
- 緩存穿透
- 緩存雪崩
- 緩存預(yù)熱
- 緩存降級(jí)
1劈猿、緩存穿透
一般訪問(wèn)緩存的流程,如果緩存中存在查詢的商品數(shù)據(jù)坎吻,那么直接返回疏哗,如果緩存中不存在,則訪問(wèn)數(shù)據(jù)庫(kù)
由于不恰當(dāng)?shù)臉I(yè)務(wù)功能實(shí)現(xiàn)禾怠,或者外部惡意攻擊不斷地請(qǐng)求某些不存在的數(shù)據(jù)內(nèi)存返奉,由于緩存中沒(méi)有保存該數(shù)據(jù),導(dǎo)致所有的請(qǐng)求都會(huì)落到數(shù)據(jù)庫(kù)上吗氏,對(duì)數(shù)據(jù)庫(kù)可能帶來(lái)一定的壓力芽偏,甚至奔潰。
解決方案:
針對(duì)緩存穿透的情況弦讽,簡(jiǎn)單是對(duì)策就是將不存在的數(shù)據(jù)訪問(wèn)結(jié)果污尉,也存儲(chǔ)到緩存中,可以有效的避免緩存穿透的風(fēng)險(xiǎn)往产。但是這樣的話被碗,可能會(huì)浪費(fèi)大量的內(nèi)存,那么可以使用布隆過(guò)濾器來(lái)節(jié)省內(nèi)存空間仿村,redis本身也是支持這種方式锐朴,布隆過(guò)濾器的介紹見海量數(shù)據(jù)下的去重和查重(二):布隆過(guò)濾器
同時(shí),為了避免無(wú)效key過(guò)多蔼囊,key要有規(guī)則焚志,如果不滿足key生成規(guī)則,則直接返回畏鼓。
2酱酬、緩存雪崩
當(dāng)緩存重啟或者大量的緩存在某一時(shí)間段失效,這樣就導(dǎo)致大批流量直接訪問(wèn)數(shù)據(jù)庫(kù)云矫,對(duì)DB造成壓力膳沽,從而引起DB故障,系統(tǒng)奔潰。
舉例來(lái)說(shuō)挑社,我們?cè)跍?zhǔn)備一項(xiàng)搶購(gòu)的促銷運(yùn)營(yíng)活動(dòng)呵俏,活動(dòng)期間將帶來(lái)大量的商品信息,庫(kù)存等相關(guān)信息的查詢滔灶,為了避免商品數(shù)據(jù)庫(kù)的壓力,將商品數(shù)據(jù)放入緩存中存儲(chǔ)吼肥,不巧的是录平,搶購(gòu)活動(dòng)期間,大量的熱門商品緩存同時(shí)失效過(guò)期了缀皱,導(dǎo)致很大的查詢流量落到了數(shù)據(jù)庫(kù)之上斗这,對(duì)于數(shù)據(jù)庫(kù)造成了很大的壓力。
解決方案
- 將商品根據(jù)品類熱度分類啤斗,購(gòu)買比價(jià)多的類目緩存周期長(zhǎng)一些表箭,購(gòu)買相對(duì)冷門的類目商品,緩存周期短一些钮莲;
- 在設(shè)置商品具體的緩存生效時(shí)間的時(shí)候免钻,加上一個(gè)隨機(jī)的區(qū)間因子,比如說(shuō)5—10分鐘之間來(lái)隨意選擇失效時(shí)間崔拥;
- 提前預(yù)估DB能力极舔,如果緩存掛掉,數(shù)據(jù)庫(kù)仍可以在一定程度上抗住流量的壓力
這三個(gè)策略能夠有效的避免短時(shí)間被链瓦,大批量的緩存失效的問(wèn)題拆魏。
3、緩存預(yù)熱
緩存預(yù)熱就是系統(tǒng)上線后慈俯,將相關(guān)的緩存數(shù)據(jù)直接加載到緩存系統(tǒng)渤刃,這樣就避免在用戶請(qǐng)求的時(shí)候,先查詢數(shù)據(jù)庫(kù)贴膘,然后再將數(shù)據(jù)緩存的問(wèn)題卖子,用戶直接查詢事先被預(yù)熱的緩存數(shù)據(jù)。
如果不進(jìn)行預(yù)熱刑峡,那么redis初始狀態(tài)數(shù)據(jù)為空揪胃,系統(tǒng)上線初期,對(duì)于高并發(fā)的流量氛琢,都會(huì)訪問(wèn)到數(shù)據(jù)庫(kù)中喊递,對(duì)數(shù)據(jù)庫(kù)造成流量的壓力。
解決方案
- 1阳似、數(shù)據(jù)量不大的時(shí)候骚勘,工程啟動(dòng)的時(shí)候進(jìn)行加載緩存動(dòng)作
- 2、數(shù)據(jù)量大的時(shí)候,設(shè)置一個(gè)定時(shí)任務(wù)腳本俏讹,進(jìn)行緩存的刷新
- 3当宴、數(shù)據(jù)量太大的時(shí)候,優(yōu)先保證熱點(diǎn)數(shù)據(jù)進(jìn)行提前加載到緩存
4泽疆、緩存降級(jí)
降級(jí)的情況户矢,就是緩存失效或者緩存服務(wù)掛掉的情況下,我們也不去訪問(wèn)數(shù)據(jù)庫(kù)殉疼,我們直接訪問(wèn)內(nèi)存部分?jǐn)?shù)據(jù)緩存梯浪,或者直接返回默認(rèn)數(shù)據(jù)。
舉例來(lái)說(shuō):
對(duì)于應(yīng)用的首頁(yè)瓢娜,一般是訪問(wèn)量非常大的地方挂洛,首頁(yè)里面往往包含了部分推薦商品的展示信息,這些推薦商品都會(huì)放到緩存中進(jìn)行存儲(chǔ)眠砾,同時(shí)我們?yōu)榱吮苊饩彺娴漠惓G闆r虏劲,對(duì)熱點(diǎn)數(shù)據(jù)也存儲(chǔ)到了內(nèi)存中,同時(shí)內(nèi)存中還保留了一些默認(rèn)的商品信息褒颈,
降級(jí)一般是有損的操作柒巫,所以盡量減少降級(jí)對(duì)于業(yè)務(wù)的影響程度。
5谷丸、數(shù)據(jù)傾斜 bigkey
數(shù)據(jù)傾斜吻育,是指大量的數(shù)據(jù)都集中到集群的某一個(gè)節(jié)點(diǎn)上,導(dǎo)致那個(gè)節(jié)點(diǎn)內(nèi)存和cpu使用率都顯著升高淤井。
通常都是由于不合理的數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)導(dǎo)致的布疼。
Redis 中大key的危害,大key帶來(lái)的危害體現(xiàn)在三個(gè)方面:
- 內(nèi)存空間不均勻币狠;
- 操作耗時(shí)游两,存在線程阻塞風(fēng)險(xiǎn);
- 網(wǎng)絡(luò)阻塞漩绵,每次獲取大key產(chǎn)生的網(wǎng)絡(luò)流量較大贱案。
如果一個(gè)key的大小為1MB,每秒訪問(wèn)量為1000止吐,那么每秒會(huì)產(chǎn)生1000MB的流量宝踪。這對(duì)于普通千兆網(wǎng)卡的服務(wù)器來(lái)說(shuō)是災(zāi)難性的。
- 網(wǎng)絡(luò)阻塞漩绵,每次獲取大key產(chǎn)生的網(wǎng)絡(luò)流量較大贱案。
redis-cli --bigkeys 命令可以統(tǒng)計(jì)bigkey的分布情況碍扔。
如果bigkey是不可避免的瘩燥,比如string類型的大key,那么建議不要存入Redis不同,因?yàn)槊看握{(diào)用都會(huì)產(chǎn)生比較嚴(yán)重的性能影響厉膀,而是放到內(nèi)存中溶耘,通過(guò)內(nèi)存緩存。
- 生產(chǎn)案例1
有個(gè)活動(dòng)上線后服鹅,發(fā)現(xiàn)有個(gè)redis單節(jié)點(diǎn)內(nèi)存使用率在不斷的增加
在查詢相關(guān)代碼時(shí)凳兵,發(fā)現(xiàn)緩存用戶參與數(shù)據(jù)用的是hash結(jié)構(gòu),field為userId企软,那么隨著時(shí)間的增加庐扫,參與用戶越來(lái)越多,key保持不變仗哨,但是value會(huì)越來(lái)越大
優(yōu)化:hash結(jié)構(gòu)拆分為string結(jié)構(gòu)形庭,把userId拼接到key上,這樣可以把key打散
- 生產(chǎn)案例2
有個(gè)需要根據(jù)策略計(jì)算給用戶發(fā)券的接口,策略可能有幾十個(gè)(產(chǎn)品配置)藻治,把這些策略的基本信息緩存到redis后,發(fā)現(xiàn)value有60k大小巷挥,在業(yè)務(wù)高峰期桩卵,請(qǐng)求量暴漲,直接把redis cpu打滿倍宾,導(dǎo)致阻塞雏节。
優(yōu)化:把這些配置信息放到內(nèi)存中,優(yōu)化從內(nèi)存中獲取高职,只有內(nèi)存不存在钩乍,才去redis中獲取
- 生產(chǎn)案例3
有個(gè)活動(dòng)必須要把要把用數(shù)據(jù)設(shè)計(jì)成hash結(jié)構(gòu),userId作為field怔锌,但是為了降低內(nèi)存使用率寥粹,使用了定時(shí)任務(wù)每天定時(shí)清理一次緩存,即刪除key埃元,在有一次業(yè)務(wù)高峰期時(shí)涝涤,redis內(nèi)存預(yù)警,為了減少空間岛杀,手動(dòng)把這個(gè)bigkey刪除了阔拳,因?yàn)関alue有7.5G大小,redis 刪除操作耗時(shí)60s类嗤,直接導(dǎo)致線程阻塞糊肠,服務(wù)不可用。
優(yōu)化:如果bigkey由于某種原因是必須的遗锣,但是當(dāng)我們用完后需要?jiǎng)h除時(shí)(比如定時(shí)任務(wù)預(yù)熱緩存時(shí)货裹,需要先刪除bigkey,然后再set數(shù)據(jù))精偿,不要直接del刪除bigkey泪酱,因?yàn)閯h除時(shí)間會(huì)過(guò)長(zhǎng),導(dǎo)致阻塞其他客戶端命令。
對(duì)于4.0版本上且開啟了異步刪除(即lazyfree-lazy-expire=yes)墓阀,可以設(shè)置過(guò)期時(shí)間自動(dòng)刪除 Expire KEY 0毡惜;
對(duì)于4.0版本以下的,就算設(shè)置過(guò)期時(shí)間也會(huì)觸發(fā)del 操作(同步刪除)斯撮,而且因?yàn)檫^(guò)期時(shí)間觸發(fā)del導(dǎo)致的阻塞经伙,不會(huì)出現(xiàn)在慢查詢中,所以有時(shí)候遇到問(wèn)題都不好排查勿锅。
bigkey的刪除見上一張第九節(jié) Redis(二):實(shí)戰(zhàn)場(chǎng)景及實(shí)現(xiàn)方式
6帕膜、熱key
在一些臨時(shí)活動(dòng)中(比如每年中秋 端午商家都會(huì)搞很多活動(dòng),每年都不一樣)溢十,為了保證活動(dòng)靈活性垮刹,我們通常會(huì)有大量配置信息(開關(guān),活動(dòng)參與人數(shù)张弛,活動(dòng)時(shí)間)荒典,為了性能,這些配置信息通常會(huì)放在redis中吞鸭,但是由于每個(gè)參與用戶都需要去redis中請(qǐng)求這些配置信息寺董,所以高峰期大量請(qǐng)求還是會(huì)打到redis集群某個(gè)節(jié)點(diǎn)上,這樣集群的性能就限制在單點(diǎn)上了刻剥。
優(yōu)化:通常這種熱key數(shù)據(jù)量都不大遮咖,可以直接緩存在內(nèi)存中,然后可以通過(guò)定時(shí)任務(wù)定時(shí)去清理造虏,或者在管理端添加一個(gè)熱key管理頁(yè)面御吞,這樣可以手動(dòng)去清理。