歡迎來到飛之折翼的簡書,待雙翼飽滿就是飛翔之際卵牍!
前言
Redis緩存的使用,極大的提升了應用程序的性能和效率沦泌,特別是數(shù)據(jù)查詢方面糊昙。
但同時,它也帶來了一些問題谢谦。其中释牺,最要害的問題,就是數(shù)據(jù)的一致性問題回挽,從嚴格意義上講,這個問題無解。如果對數(shù)據(jù)的一致性要求很高讹堤,那么就不能使用緩存辛孵。
另外的一些典型問題就是,緩存穿透墙牌、緩存雪崩和緩存擊穿涡驮,這是設計一個緩存系統(tǒng),不得不要考慮的問題喜滨!
緩存處理流程
正常的一個緩存處理流程都會這樣設計:請求數(shù)據(jù) 捉捅,后段先從緩存中g(shù)et數(shù)據(jù),get到直接返回結(jié)果虽风,get不到時從DB中取棒口,DB取到更新緩存寄月,并返回結(jié)果,DB也沒取到无牵,那直接返回空結(jié)果漾肮,流程見下圖:
但這樣很容易引起緩存穿透、緩存擊穿合敦、緩存雪崩3蹰佟!3涞骸1i堋!
緩存穿透
緩存穿透是指查詢一個一定不存在的數(shù)據(jù)崔梗,由于緩存是不命中時被動寫的夜只,并且出于容錯考慮,如果從存儲層查不到數(shù)據(jù)則不寫入緩存蒜魄,這將導致這個不存在的數(shù)據(jù)每次請求都要到存儲層去查詢扔亥,失去了緩存的意義。在流量大時谈为,可能DB就掛掉了旅挤,要是有人利用不存在的key頻繁攻擊我們的應用,這就是漏洞伞鲫。
解決方案
有很多種方法可以有效地解決緩存穿透問題:
最常見的則是采用布隆過濾器粘茄,將所有可能存在的數(shù)據(jù)哈希到一個足夠大的bitmap中,一個一定不存在的數(shù)據(jù)會被 這個bitmap攔截掉秕脓,從而避免了對底層存儲系統(tǒng)的查詢壓力柒瓣;
接口層增加校驗,如用戶鑒權(quán)校驗吠架,id做基礎校驗芙贫,id<=0的直接攔截;
另外也有一個更為簡單粗暴的方法傍药,如果一個查詢返回的數(shù)據(jù)為空磺平,仍然把這個空結(jié)果進行緩存,緩存有效時間可以設置短點怔檩,如30秒(設置太長會導致正常情況也沒法使用)褪秀。這樣可以防止攻擊用戶反復用同一個id暴力攻擊。
緩存雪崩
緩存雪崩是指在我們設置緩存時采用了相同的過期時間薛训,導致緩存在某一時刻同時失效媒吗,請求全部轉(zhuǎn)發(fā)到DB,DB瞬時壓力過重雪崩乙埃。
解決方案
緩存數(shù)據(jù)的過期時間設置隨機闸英,防止同一時間大量數(shù)據(jù)過期現(xiàn)象發(fā)生锯岖。
如果緩存數(shù)據(jù)庫是分布式部署,將熱點數(shù)據(jù)均勻分布在不同搞得緩存數(shù)據(jù)庫中甫何。
設置熱點數(shù)據(jù)永遠不過期出吹。
緩存擊穿
對于一些設置了過期時間的key,如果這些key可能會在某些時間點被超高并發(fā)地訪問辙喂,是一種非炒防危“熱點”的數(shù)據(jù)。這個時候巍耗,需要考慮一個問題:緩存被“擊穿”的問題秋麸,這個和緩存雪崩的區(qū)別在于這里針對某一key緩存,前者則是很多key炬太。
解決方案
1.使用互斥鎖(mutex key)
這種解決方案思路比較簡單灸蟆,就是只讓一個線程構(gòu)建緩存,其他線程等待構(gòu)建緩存的線程執(zhí)行完亲族,重新從緩存獲取數(shù)據(jù)就可以炒考,如下圖:
2."提前"使用互斥鎖(mutex key)
在value內(nèi)部設置1個超時值(timeout1), timeout1比實際的memcache timeout(timeout2)小。當從cache讀取到timeout1發(fā)現(xiàn)它已經(jīng)過期時候霎迫,馬上延長timeout1并重新設置到cache斋枢。然后再從數(shù)據(jù)庫加載數(shù)據(jù)并設置到cache中。
3."永遠不過期"
這里的“永遠不過期”包含兩層意思:
a.從redis上看知给,確實沒有設置過期時間杏慰,這就保證了,不會出現(xiàn)熱點key過期問題炼鞠,也就是“物理”不過期。
b. 從功能上看轰胁,如果不過期谒主,那不就成靜態(tài)的了嗎?所以我們把過期時間存在key對應的value里赃阀,如果發(fā)現(xiàn)要過期了霎肯,通過一個后臺的異步線程進行緩存的構(gòu)建,也就是“邏輯”過期
方案對比
總結(jié)
沒有最好榛斯,只有更好观游!
只有熟悉自己的業(yè)務系統(tǒng),才能找到最合適自己系統(tǒng)的解決方案驮俗。