緩存問題總結(jié)
前言
通常應(yīng)用中會使用數(shù)據(jù)庫來作為數(shù)據(jù)存儲,但是數(shù)據(jù)庫面向磁盤,磁盤的讀寫速度比較慢,不合適處理高并發(fā)的應(yīng)用場景,為了避免高并發(fā)場景下瞬間大量訪問DB導(dǎo)致數(shù)據(jù)庫系統(tǒng)癱瘓的問題,我們通常會使用緩存來提高系統(tǒng)支持高并發(fā)的能力。緩存一般都是基于內(nèi)存的數(shù)據(jù)庫,讀寫效率相比于磁盤讀寫都有大幅提升,但引入緩存后相應(yīng)也會引入一些緩存相關(guān)的問題。
緩存問題
- 緩存穿透
- 緩存擊穿
- 緩存血崩
- 緩存溢出
- 數(shù)據(jù)丟失
下面我們以redis為例,來分析這些問題并給出對應(yīng)的解決方案。
問題分析
緩存穿透
緩存穿透指的是數(shù)據(jù)源(通常是DB)中,沒有key對應(yīng)的數(shù)據(jù)源,因此請求這類不存在的key,在緩存中獲取不到的話就需要直接請求數(shù)據(jù)源,請求并發(fā)量大的話就會給數(shù)據(jù)源造成壓力和風(fēng)險(xiǎn)。
針對這類問題,有兩個常用的解決方案:
1.即使查詢數(shù)據(jù)源沒有該key對應(yīng)的值,也在緩存中緩存該key,并設(shè)置空的value值。需要注意的是,必須給這種key設(shè)置一個短的過期時(shí)間,在這段時(shí)間內(nèi)數(shù)據(jù)源新增了這個key對應(yīng)的記錄但是獲取不到的情況。由此可見,該方案存在一個問題就是新增的記錄可能存在不能立即生效的問題。
2.第二種方案是使用布隆過濾器,將可能的key值數(shù)據(jù)取哈希存儲到一個bitmap中,通過判斷key值是否在bitmap內(nèi)來避免向數(shù)據(jù)源請求不存在的記錄。
緩存擊穿
緩存擊穿和緩存穿透不同,緩存穿透是請求不存在key記錄,而緩存擊穿請求的key在數(shù)據(jù)源中是有對應(yīng)的記錄的。對于熱點(diǎn)數(shù)據(jù)的key,如果剛好在緩存過期的時(shí)間后有大量并發(fā)請求,就會給數(shù)據(jù)源造成服務(wù)崩潰風(fēng)險(xiǎn)。
針對這一問題,通常的方案是加互斥鎖,只有獲取鎖的線程可以請求數(shù)據(jù)源,其他請求可以等待重試獲取緩存。
對于redis來說,可以通過SETNX操作來設(shè)置一個互斥key來實(shí)現(xiàn)互斥鎖,設(shè)置成功則代表成功獲取鎖。需要注意的是需要給互斥key來設(shè)置一個過期時(shí)間,避免死鎖問題。
此外,也可以設(shè)置熱點(diǎn)數(shù)據(jù)不過期來解決這類問題。
緩存雪崩
緩存擊穿是針對某一個key過期時(shí)的并發(fā)問題,而緩存雪崩是緩存服務(wù)器重啟或者大量緩存集中在一個時(shí)間段過期,導(dǎo)致大量并發(fā)請求直接打到數(shù)據(jù)源,造成數(shù)據(jù)源服務(wù)崩潰。
緩存雪崩的幾個解決方案:
- 采用隨機(jī)的過期時(shí)間。可以在設(shè)置緩存過期時(shí)間時(shí),盡量分散各個key的過期時(shí)間避免集中過期的場景
- 使用鎖或者隊(duì)列。通過鎖或者隊(duì)列來避免所有請求直接請求數(shù)據(jù)庫。這種會導(dǎo)致線程阻塞,可能影響用戶體驗(yàn)。
- 緩存標(biāo)記。通過記錄緩存數(shù)據(jù)是否過期,如果過期則通知專門的線程來處理實(shí)際key的更新。
緩存溢出
緩存使用的是內(nèi)存資源,相比于磁盤存儲來說可以緩存的數(shù)據(jù)有限的。緩存溢出是指緩存的數(shù)據(jù)達(dá)到上限,無法緩存更多的數(shù)據(jù)。
這種問題的解決方案通常是給緩存設(shè)置一定的淘汰策略,一般使用LRU(Least Recently Used)。最近最少使用的key會被優(yōu)先淘汰。
數(shù)據(jù)丟失
緩存數(shù)據(jù)在內(nèi)存中,如果緩存服務(wù)器宕機(jī)或者重啟,內(nèi)存中的數(shù)據(jù)就會丟失。
針對這一問題,主要依賴于緩存服務(wù)的持久化。redis提供了兩種數(shù)據(jù)持久化的方案,一種是RDB,一種是AOF。
RDB是在指定的時(shí)間間隔內(nèi)生成數(shù)據(jù)集的時(shí)間點(diǎn)快照,是一個非常緊湊的文件,適用于災(zāi)難恢復(fù),同時(shí)數(shù)據(jù)備份時(shí)只需要分配一個子進(jìn)程來處理數(shù)據(jù)保存工作,父進(jìn)程無需執(zhí)行磁盤IO操作,可以最大化Redis的性能。
AOF的話可以讓Redis非常耐久,盡可能短的減少數(shù)據(jù)丟失問題。但是備份文件體積要大于RDB,性能及恢復(fù)效率上不如RDB。
轉(zhuǎn)載請注明出處,如有錯誤的地方請留言給我更正,謝謝!