經(jīng)典面試題:一文了解常見的緩存問題
在面試過程中,面試官的桌子上擺放著很多高頻的面試題,能否順利回答決定了你面試通過的概率。其中緩存問題就是其中的一份,可以說掌握緩存問題及解決方法是面試前必須準(zhǔn)備的內(nèi)容。那么緩存有什么典型的問題,出現(xiàn)的原因是什么,又該如何解決呢?本文,來為你一一詳細(xì)介紹。

緩存問題有哪些?
緩存雖然能提升性能,但也會(huì)帶來一些問題。緩存問題有很多,其中經(jīng)典的緩存問題如下圖所示:

1. 緩存雪崩
問題描述:緩存服務(wù)宕機(jī),導(dǎo)致所有請(qǐng)求直接訪問數(shù)據(jù)庫,引發(fā)數(shù)據(jù)庫壓力激增甚至崩潰。
解決方法:
- 集群化部署緩存(如Redis Cluster),實(shí)現(xiàn)高可用;
- 使用熔斷降級(jí)機(jī)制,限制數(shù)據(jù)庫訪問量;
- 讀寫分離;
- 使用本地緩存;
- 對(duì)緩存體系進(jìn)行實(shí)時(shí)監(jiān)控,當(dāng)請(qǐng)求訪問的慢速比超過閥值時(shí),及時(shí)報(bào)警,通過機(jī)器替換、服務(wù)替換進(jìn)行及時(shí)恢復(fù);也可以通過各種自動(dòng)故障轉(zhuǎn)移策略,自動(dòng)關(guān)閉異常接口、停止邊緣服務(wù)、停止部分非核心功能措施,確保在極端場景下,核心功能的正常運(yùn)行。
2. 緩存失效
問題描述:大量緩存數(shù)據(jù)同時(shí)過期,導(dǎo)致所有請(qǐng)求直接訪問數(shù)據(jù)庫,引發(fā)數(shù)據(jù)庫壓力激增甚至崩潰。
解決方法:設(shè)置隨機(jī)過期時(shí)間,避免同時(shí)失效。使用公式:過期時(shí)間 = base 時(shí)間 + 隨機(jī)時(shí)間。
3. 緩存穿透
問題描述:頻繁查詢不存在的數(shù)據(jù)(如惡意攻擊),緩存和數(shù)據(jù)庫均無法命中,導(dǎo)致無效請(qǐng)求穿透到數(shù)據(jù)庫。
解決方法:
- 布隆過濾器(Bloom Filter): 構(gòu)建一個(gè) BloomFilter 緩存過濾器,記錄全量數(shù)據(jù),這樣訪問數(shù)據(jù)時(shí),可以直接通過 BloomFilter 判斷這個(gè) key 是否存在,如果不存在直接返回即可,根本無需查緩存和 DB。但是BloomFilter 要緩存全量的 key,這就要求全量的 key 數(shù)量不大,10億條數(shù)據(jù)以內(nèi)最佳,因?yàn)?10億 條數(shù)據(jù)大概要占用 1.2GB 的內(nèi)存。也可以用 BloomFilter 緩存非法 key,每次發(fā)現(xiàn)一個(gè) key 是不存在的非法 key,就記錄到 BloomFilter 中,這種記錄方案,會(huì)導(dǎo)致 BloomFilter 存儲(chǔ)的 key 持續(xù)高速增長,為了避免記錄 key 太多而導(dǎo)致誤判率增大,需要定期清零處理;
- 緩存空值(Null Object): 為不存在的 Key 設(shè)置短時(shí)間緩存,避免重復(fù)查詢數(shù)據(jù)庫。
4. 緩存擊穿
問題描述:某個(gè)熱點(diǎn)Key突然過期,大量并發(fā)請(qǐng)求直接訪問數(shù)據(jù)庫,導(dǎo)致瞬時(shí)壓力過大。
解決方法:
- 永久緩存: 針對(duì)基本不會(huì)發(fā)生更新的場景,可以把 key 設(shè)置為永不過期,讓 key 常駐緩存;定期緩存:針對(duì)需要頻繁更新的場景,可以使用額外的補(bǔ)償程序來定時(shí)刷新緩存或者延長 key 的實(shí)效時(shí)間;
- 分布式鎖: 針對(duì)偶爾需要更新的場景,可以對(duì)請(qǐng)求代碼使用分布式互斥鎖,使得少部分直接請(qǐng)求數(shù)據(jù)庫后更新緩存,而剩余的其他請(qǐng)求直接使用新緩存即可,或者采用本地互斥鎖保證僅有少量請(qǐng)求能夠更新緩存,其余請(qǐng)求訪問新緩存。
5. 緩存與數(shù)據(jù)庫一致性
問題描述:緩存與數(shù)據(jù)庫數(shù)據(jù)不一致,常見于更新操作時(shí),比如更新 DB 后,寫緩存失敗,從而導(dǎo)致緩存中存的是老數(shù)據(jù)。
解決方式:
- 刪除 Key: 寫入/更新的時(shí)候,先刪除緩存中的 Key,再更新數(shù)據(jù)庫;
- 訂閱數(shù)據(jù)庫Binlog: 通過監(jiān)聽數(shù)據(jù)庫變更同步更新緩存(如Canal工具);
- 最終一致性容忍: 根據(jù)業(yè)務(wù)場景接受短暫不一致。
6. 緩存預(yù)熱
問題描述:系統(tǒng)啟動(dòng)時(shí)緩存為空,大量請(qǐng)求直接訪問數(shù)據(jù)庫導(dǎo)致冷啟動(dòng)壓力。
解決方式:提前加載熱點(diǎn)數(shù)據(jù)到緩存(如統(tǒng)計(jì)分析高頻訪問的Key)。
7. 緩存淘汰策略
問題描述:緩存空間有限時(shí),如何選擇淘汰哪些數(shù)據(jù)以騰出空間。
解決方式:
- LRU(Least Recently Used): 淘汰最近最少使用的數(shù)據(jù);
- LFU(Least Frequently Used): 淘汰訪問頻率最低的數(shù)據(jù);
- TTL(Time To Live):基于過期時(shí)間淘汰。
8. 緩存污染
問題描述:緩存中存儲(chǔ)了低頻訪問的數(shù)據(jù),擠占了熱點(diǎn)數(shù)據(jù)的空間。
解決方式:
優(yōu)化緩存淘汰策略(如結(jié)合LRU和LFU);
定期清理非熱點(diǎn)數(shù)據(jù)。
9. 熱點(diǎn) Key
問題描述:某些業(yè)務(wù)在某一瞬間或某一時(shí)間段內(nèi)可能會(huì)成為熱點(diǎn)業(yè)務(wù),熱點(diǎn)業(yè)務(wù)的數(shù)據(jù)可能會(huì)產(chǎn)生熱點(diǎn)key,比如微博上熱榜數(shù)據(jù)。
解決方式:
- 找到對(duì)應(yīng)的熱點(diǎn) key,將這些熱 key 進(jìn)行分散處理,比如一個(gè)熱 key 名字叫 hotkey,可以被分散為 hotkey#1、hotkey#2、hotkey#3,……h(huán)otkey#n,這 n 個(gè) key 分散存在多個(gè)緩存節(jié)點(diǎn),然后 client 端請(qǐng)求時(shí),隨機(jī)訪問其中某個(gè)后綴的 hotkey,這樣就可以把熱 key 的請(qǐng)求打散,避免一個(gè)緩存節(jié)點(diǎn)過載;
- 也可以 key 的名字不變,對(duì)緩存提前進(jìn)行多副本+多級(jí)結(jié)合的緩存架構(gòu)設(shè)計(jì)。再次,如果熱 key 較多,還可以通過監(jiān)控體系對(duì)緩存的 SLA 實(shí)時(shí)監(jiān)控,通過快速擴(kuò)容來減少熱 key 的沖擊。最后,業(yè)務(wù)端還可以使用本地緩存,將這些熱 key 記錄在本地緩存,來減少對(duì)遠(yuǎn)程緩存的沖擊。
10. 大 Key
問題描述:緩存中某些 key 的 value 的值過大,導(dǎo)致寫操作超時(shí)、加載速度緩慢等問題。
解決方式:
- 如果數(shù)據(jù)存在 MC 中,可以設(shè)計(jì)一個(gè)緩存閥值,當(dāng) value 的長度超過閥值,則對(duì)內(nèi)容啟用壓縮,讓 KV 盡量保持小的 size,其次評(píng)估大 key 所占的比例,在 Mc 啟動(dòng)之初,就立即預(yù)寫足夠數(shù)據(jù)的大 key,讓 MC 預(yù)先分配足夠多的 trunk size 較大的 slab。確保后面系統(tǒng)運(yùn)行時(shí),大 key 有足夠的空間來進(jìn)行緩存;
- 如果數(shù)據(jù)存在 Redis 中,比如業(yè)務(wù)數(shù)據(jù)存 set 格式,大 key 對(duì)應(yīng)的 set 結(jié)構(gòu)有幾千幾萬個(gè)元素,這種寫入 Redis 時(shí)會(huì)消耗很長的時(shí)間,導(dǎo)致 Redis 卡頓。此時(shí),可以擴(kuò)展新的數(shù)據(jù)結(jié)構(gòu),同時(shí)讓 client 在這些大 key 寫緩存之前,進(jìn)行序列化構(gòu)建,然后通過 restore 一次性寫入;
- 將大 key 分拆為多個(gè) key,盡量減少大 key 的存在。同時(shí)由于大 key 一旦穿透到 DB,加載耗時(shí)很大,所以可以對(duì)這些大 key 進(jìn)行特殊照顧,比如設(shè)置較長的過期時(shí)間,比如緩存內(nèi)部在淘汰 key 時(shí),同等條件下,盡量不淘汰這些大 key。






























