前言
現(xiàn)在的網(wǎng)站基本上都會加入緩存層來提升性能表現(xiàn)锻霎,前端請求會先去緩存層請求數(shù)據(jù),若未命中緩存才會去請求數(shù)據(jù)庫層吼旧,這樣可以大大減少去數(shù)據(jù)庫層請求的連接數(shù)梨与,減少數(shù)據(jù)庫壓力堕花,加快接口響應(yīng)速度。
緩存雖然是性能利器粥鞋,但在高并發(fā)情況下使用不當可能會導(dǎo)致以下幾個問題缘挽。
緩存穿透
定義:以最常用的緩存中間件Redis為例,我們將緩存數(shù)據(jù)以K/V的形式存儲在Redis中呻粹,通過查詢key是否在Redis中存在來判斷是否命中緩存壕曼。一般情況下我們只將查詢結(jié)果不為空的數(shù)據(jù)進行緩存。緩存穿透指的是前端請求的數(shù)據(jù)在緩存中一定不存在尚猿,導(dǎo)致請求直接訪問數(shù)據(jù)庫窝稿,若惡意構(gòu)造大量這樣的請求訪問系統(tǒng),那么數(shù)據(jù)庫有可能會因為訪問量過大而被壓垮凿掂。
解決方案:
1.對查詢結(jié)果為空的key也進行緩存伴榔。當我們緩存空數(shù)據(jù)時,再有相同條件的查詢請求時庄萎,直接返回空即可而不必去請求數(shù)據(jù)庫踪少。由于導(dǎo)致結(jié)果為空值的查詢條件可能會比較多,這樣會導(dǎo)致緩存中存在大量value為空值的K/V對糠涛,為了解決這個問題援奢,我們可以將數(shù)據(jù)為空的緩存的過期時間設(shè)置的較短一些,使其快速過期忍捡,以釋放內(nèi)存空間集漾。
2.使用布隆過濾器(對布隆過濾器不清楚的同學(xué)請看我之前的這篇文章布隆過濾器)。我們按照查詢條件去查詢數(shù)據(jù)砸脊,查詢條件一定是有限的具篇,我們可以將所有可能的查詢參數(shù)組合都放入布隆過濾器中,在controller中進行校驗凌埂,在布隆過濾器中不存在的參數(shù)組合則會被過濾掉直接返回驱显。這樣連緩存層都不用訪問了。
緩存雪崩
定義:緩存雪崩指的是緩存服務(wù)器重啟或者緩存服務(wù)器宕機瞳抓,那么此時所有的請求都會到達數(shù)據(jù)庫層埃疫,導(dǎo)致數(shù)據(jù)庫被壓垮。
解決方案:使用緩存服務(wù)器集群孩哑,保證緩存的高可用栓霜,這個方案更偏向于架構(gòu)與運維一些。
熱點key
定義:熱點key其實可以看做緩存雪崩的一個子類横蜒,這里把他分出來是為了結(jié)構(gòu)更加清晰一點叙淌。一般情況下我們會給緩存設(shè)置過期時間秤掌,熱點key指的是對于一些請求量較高的熱點數(shù)據(jù)而言,一旦集中過期鹰霍,此時將會有大量請求落在數(shù)據(jù)庫層闻鉴,從而可能會導(dǎo)致數(shù)據(jù)庫崩潰∶鳎或者均訪問一臺服務(wù)器達到網(wǎng)卡上限
解決方案:
1.錯開過期時間孟岛。為了防止一批熱點key同時失效,我們可以為這些key設(shè)置不同的過期時間督勺,使其過期時間盡量錯開渠羞。
2.使用互斥鎖。利用Redis的setnx命令或者多參數(shù)的set命令實現(xiàn)互斥鎖智哀,只讓一個線程去查詢數(shù)據(jù)庫構(gòu)建緩存次询,其他線程等待該線程執(zhí)行完成,重新從緩存獲取數(shù)據(jù)就可以了瓷叫。
3.永不過期屯吊。指的是不為緩存設(shè)置過期時間,只在value中存一個邏輯上的過期時間摹菠,每次get發(fā)現(xiàn)邏輯過期時間小于當前時間則新建一個線程去數(shù)據(jù)庫重新獲取數(shù)據(jù)構(gòu)建緩存盒卸。
4.接口限流。這個方案就不涉及緩存操作相關(guān)代碼了次氨,就純粹是做接口上的限流操作蔽介。關(guān)于如何做限流請看我之前的這篇文章關(guān)于限流。
5. 使用本地緩存
6. 利用分片煮寡,將一個key備份多份虹蓄,存儲在集群的不同機器上,將訪問量均攤到所有實例幸撕。