你知道的越多,你不知道的越多
點(diǎn)贊再看,養(yǎng)成習(xí)慣
本文 GitHub https://github.com/JavaFamily 上已經(jīng)收錄,有一線大廠面試點(diǎn)思維導(dǎo)圖,也整理了很多我的文檔,歡迎Star和完善,大家面試可以參照考點(diǎn)復(fù)習(xí),希望我們一起有點(diǎn)東西。
前言
作為一個在互聯(lián)網(wǎng)公司面一次拿一次Offer的面霸,打敗了無數(shù)競爭對手,每次都只能看到無數(shù)落寞的身影失望的離開,略感愧疚(請?jiān)试S我使用一下夸張的修辭手法)。
于是在一個寂寞難耐的夜晚,我痛定思痛,決定開始寫互聯(lián)網(wǎng)技術(shù)棧面試相關(guān)的文章,希望能幫助各位讀者以后面試勢如破竹,對面試官進(jìn)行360°的反擊,吊打問你的面試官,讓一同面試的同僚瞠目結(jié)舌,瘋狂收割大廠Offer!
所有文章的名字只是我的噱頭,我們應(yīng)該有一顆謙遜的心,所以希望大家懷著空杯心態(tài)好好學(xué),一起進(jìn)步。
絮叨
上一期因?yàn)槭窃陔p十一一直在熬夜的大環(huán)境下完成的,所以我自己覺得質(zhì)量明顯沒之前的好,我這不一睡好就加班加點(diǎn)準(zhǔn)備補(bǔ)償大家,來點(diǎn)干貨。(熬夜太容易感冒了,這次點(diǎn)個贊別白嫖了!)
順帶提一嘴,我把我準(zhǔn)備寫啥畫了一個思維導(dǎo)圖,以后總不能每篇都放個賊大的圖吧,就開源到了我的GitHub,大家有興趣可以去完善和Star。
這篇我就先放出來大家看看,感覺還是差點(diǎn)意思,等大家完善了。

回望過去
上一期吊打系列我們提到了Redis相關(guān)的一些知識,還沒看的小伙伴可以回顧一下
- 《吊打面試官》系列-Redis基礎(chǔ)
- 《吊打面試官》系列-緩存雪崩、擊穿、穿透
- 《吊打面試官》系列-Redis哨兵、持久化、主從、手撕LRU
- 《吊打面試官》系列-Redis終章-凜冬將至、FPX-新王登基
這期我就從緩存到一些常見的問題講一下,有一些我是之前提到過的,不過可能大部分仔是第一次看,我就重復(fù)發(fā)一下。
緩存知識點(diǎn)
image

緩存有哪些類型?
緩存是高并發(fā)場景下提高熱點(diǎn)數(shù)據(jù)訪問性能的一個有效手段,在開發(fā)項(xiàng)目時會經(jīng)常使用到。
緩存的類型分為:本地緩存、分布式緩存和多級緩存。
本地緩存:
本地緩存就是在進(jìn)程的內(nèi)存中進(jìn)行緩存,比如我們的 JVM 堆中,可以用 LRUMap 來實(shí)現(xiàn),也可以使用 Ehcache 這樣的工具來實(shí)現(xiàn)。
本地緩存是內(nèi)存訪問,沒有遠(yuǎn)程交互開銷,性能最好,但是受限于單機(jī)容量,一般緩存較小且無法擴(kuò)展。
分布式緩存:
分布式緩存可以很好得解決這個問題。
分布式緩存一般都具有良好的水平擴(kuò)展能力,對較大數(shù)據(jù)量的場景也能應(yīng)付自如。缺點(diǎn)就是需要進(jìn)行遠(yuǎn)程請求,性能不如本地緩存。
多級緩存:
為了平衡這種情況,實(shí)際業(yè)務(wù)中一般采用多級緩存,本地緩存只保存訪問頻率最高的部分熱點(diǎn)數(shù)據(jù),其他的熱點(diǎn)數(shù)據(jù)放在分布式緩存中。
在目前的一線大廠中,這也是最常用的緩存方案,單考單一的緩存方案往往難以撐住很多高并發(fā)的場景。
淘汰策略
不管是本地緩存還是分布式緩存,為了保證較高性能,都是使用內(nèi)存來保存數(shù)據(jù),由于成本和內(nèi)存限制,當(dāng)存儲的數(shù)據(jù)超過緩存容量時,需要對緩存的數(shù)據(jù)進(jìn)行剔除。
一般的剔除策略有 FIFO 淘汰最早數(shù)據(jù)、LRU 剔除最近最少使用、和 LFU 剔除最近使用頻率最低的數(shù)據(jù)幾種策略。
noeviction:返回錯誤當(dāng)內(nèi)存限制達(dá)到并且客戶端嘗試執(zhí)行會讓更多內(nèi)存被使用的命令(大部分的寫入指令,但DEL和幾個例外)
allkeys-lru: 嘗試回收最少使用的鍵(LRU),使得新添加的數(shù)據(jù)有空間存放。
volatile-lru: 嘗試回收最少使用的鍵(LRU),但僅限于在過期集合的鍵,使得新添加的數(shù)據(jù)有空間存放。
allkeys-random: 回收隨機(jī)的鍵使得新添加的數(shù)據(jù)有空間存放。
volatile-random: 回收隨機(jī)的鍵使得新添加的數(shù)據(jù)有空間存放,但僅限于在過期集合的鍵。
-
volatile-ttl: 回收在過期集合的鍵,并且優(yōu)先回收存活時間(TTL)較短的鍵,使得新添加的數(shù)據(jù)有空間存放。
如果沒有鍵滿足回收的前提條件的話,策略volatile-lru, volatile-random以及volatile-ttl就和noeviction 差不多了。
其實(shí)在大家熟悉的LinkedHashMap中也實(shí)現(xiàn)了Lru算法的,實(shí)現(xiàn)如下:

當(dāng)容量超過100時,開始執(zhí)行LRU策略:將最近最少未使用的 TimeoutInfoHolder 對象 evict 掉。
真實(shí)面試中會讓你寫LUR算法,你可別搞原始的那個,那真TM多,寫不完的,你要么懟上面這個,要么懟下面這個,找一個數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn)下Java版本的LRU還是比較容易的,知道啥原理就好了。

Memcache
注意后面會把 Memcache 簡稱為 MC。
先來看看 MC 的特點(diǎn):
- MC 處理請求時使用多線程異步 IO 的方式,可以合理利用 CPU 多核的優(yōu)勢,性能非常優(yōu)秀;
- MC 功能簡單,使用內(nèi)存存儲數(shù)據(jù);
- MC 的內(nèi)存結(jié)構(gòu)以及鈣化問題我就不細(xì)說了,大家可以查看官網(wǎng)了解下;
- MC 對緩存的數(shù)據(jù)可以設(shè)置失效期,過期后的數(shù)據(jù)會被清除;
- 失效的策略采用延遲失效,就是當(dāng)再次使用數(shù)據(jù)時檢查是否失效;
- 當(dāng)容量存滿時,會對緩存中的數(shù)據(jù)進(jìn)行剔除,剔除時除了會對過期 key 進(jìn)行清理,還會按 LRU 策略對數(shù)據(jù)進(jìn)行剔除。
另外,使用 MC 有一些限制,這些限制在現(xiàn)在的互聯(lián)網(wǎng)場景下很致命,成為大家選擇Redis、MongoDB的重要原因:
- key 不能超過 250 個字節(jié);
- value 不能超過 1M 字節(jié);
- key 的最大失效時間是 30 天;
- 只支持 K-V 結(jié)構(gòu),不提供持久化和主從同步功能。
Redis
先簡單說一下 Redis 的特點(diǎn),方便和 MC 比較。
- 與 MC 不同的是,Redis 采用單線程模式處理請求。這樣做的原因有 2 個:一個是因?yàn)椴捎昧朔亲枞漠惒绞录幚頇C(jī)制;另一個是緩存數(shù)據(jù)都是內(nèi)存操作 IO 時間不會太長,單線程可以避免線程上下文切換產(chǎn)生的代價。
- Redis 支持持久化,所以 Redis 不僅僅可以用作緩存,也可以用作 NoSQL 數(shù)據(jù)庫。
- 相比 MC,Redis 還有一個非常大的優(yōu)勢,就是除了 K-V 之外,還支持多種數(shù)據(jù)格式,例如 list、set、sorted set、hash 等。
- Redis 提供主從同步機(jī)制,以及 Cluster 集群部署能力,能夠提供高可用服務(wù)。
詳解 Redis
Redis 的知識點(diǎn)結(jié)構(gòu)如下圖所示。

功能
來看 Redis 提供的功能有哪些吧!
我們先看基礎(chǔ)類型:
String:
String 類型是 Redis 中最常使用的類型,內(nèi)部的實(shí)現(xiàn)是通過 SDS(Simple Dynamic String )來存儲的。SDS 類似于 Java 中的 ArrayList,可以通過預(yù)分配冗余空間的方式來減少內(nèi)存的頻繁分配。
這是最簡單的類型,就是普通的 set 和 get,做簡單的 KV 緩存。
但是真實(shí)的開發(fā)環(huán)境中,很多仔可能會把很多比較復(fù)雜的結(jié)構(gòu)也統(tǒng)一轉(zhuǎn)成String去存儲使用,比如有的仔他就喜歡把對象或者List轉(zhuǎn)換為JSONString進(jìn)行存儲,拿出來再反序列話啥的。
我在這里就不討論這樣做的對錯了,但是我還是希望大家能在最合適的場景使用最合適的數(shù)據(jù)結(jié)構(gòu),對象找不到最合適的但是類型可以選最合適的嘛,之后別人接手你的代碼一看這么規(guī)范,誒這小伙子有點(diǎn)東西呀,看到你啥都是用的String,垃圾!

好了這些都是題外話了,道理還是希望大家記在心里,習(xí)慣成自然嘛,小習(xí)慣成就你。
String的實(shí)際應(yīng)用場景比較廣泛的有:
緩存功能:String字符串是最常用的數(shù)據(jù)類型,不僅僅是Redis,各個語言都是最基本類型,因此,利用Redis作為緩存,配合其它數(shù)據(jù)庫作為存儲層,利用Redis支持高并發(fā)的特點(diǎn),可以大大加快系統(tǒng)的讀寫速度、以及降低后端數(shù)據(jù)庫的壓力。
計(jì)數(shù)器:許多系統(tǒng)都會使用Redis作為系統(tǒng)的實(shí)時計(jì)數(shù)器,可以快速實(shí)現(xiàn)計(jì)數(shù)和查詢的功能。而且最終的數(shù)據(jù)結(jié)果可以按照特定的時間落地到數(shù)據(jù)庫或者其它存儲介質(zhì)當(dāng)中進(jìn)行永久保存。
共享用戶Session:用戶重新刷新一次界面,可能需要訪問一下數(shù)據(jù)進(jìn)行重新登錄,或者訪問頁面緩存Cookie,但是可以利用Redis將用戶的Session集中管理,在這種模式只需要保證Redis的高可用,每次用戶Session的更新和獲取都可以快速完成。大大提高效率。
Hash:
這個是類似 Map 的一種結(jié)構(gòu),這個一般就是可以將結(jié)構(gòu)化的數(shù)據(jù),比如一個對象(前提是這個對象沒嵌套其他的對象)給緩存在 Redis 里,然后每次讀寫緩存的時候,可以就操作 Hash 里的某個字段。
但是這個的場景其實(shí)還是多少單一了一些,因?yàn)楝F(xiàn)在很多對象都是比較復(fù)雜的,比如你的商品對象可能里面就包含了很多屬性,其中也有對象。我自己使用的場景用得不是那么多。
List:
List 是有序列表,這個還是可以玩兒出很多花樣的。
比如可以通過 List 存儲一些列表型的數(shù)據(jù)結(jié)構(gòu),類似粉絲列表、文章的評論列表之類的東西。
比如可以通過 lrange 命令,讀取某個閉區(qū)間內(nèi)的元素,可以基于 List 實(shí)現(xiàn)分頁查詢,這個是很棒的一個功能,基于 Redis 實(shí)現(xiàn)簡單的高性能分頁,可以做類似微博那種下拉不斷分頁的東西,性能高,就一頁一頁走。
比如可以搞個簡單的消息隊(duì)列,從 List 頭懟進(jìn)去,從 List 屁股那里弄出來。
List本身就是我們在開發(fā)過程中比較常用的數(shù)據(jù)結(jié)構(gòu)了,熱點(diǎn)數(shù)據(jù)更不用說了。
消息隊(duì)列:Redis的鏈表結(jié)構(gòu),可以輕松實(shí)現(xiàn)阻塞隊(duì)列,可以使用左進(jìn)右出的命令組成來完成隊(duì)列的設(shè)計(jì)。比如:數(shù)據(jù)的生產(chǎn)者可以通過Lpush命令從左邊插入數(shù)據(jù),多個數(shù)據(jù)消費(fèi)者,可以使用BRpop命令阻塞的“搶”列表尾部的數(shù)據(jù)。
-
文章列表或者數(shù)據(jù)分頁展示的應(yīng)用。
比如,我們常用的博客網(wǎng)站的文章列表,當(dāng)用戶量越來越多時,而且每一個用戶都有自己的文章列表,而且當(dāng)文章多時,都需要分頁展示,這時可以考慮使用Redis的列表,列表不但有序同時還支持按照范圍內(nèi)獲取元素,可以完美解決分頁查詢功能。大大提高查詢效率。
Set:
Set 是無序集合,會自動去重的那種。
直接基于 Set 將系統(tǒng)里需要去重的數(shù)據(jù)扔進(jìn)去,自動就給去重了,如果你需要對一些數(shù)據(jù)進(jìn)行快速的全局去重,你當(dāng)然也可以基于 JVM 內(nèi)存里的 HashSet 進(jìn)行去重,但是如果你的某個系統(tǒng)部署在多臺機(jī)器上呢?得基于Redis進(jìn)行全局的 Set 去重。
可以基于 Set 玩兒交集、并集、差集的操作,比如交集吧,我們可以把兩個人的好友列表整一個交集,看看倆人的共同好友是誰?對吧。
反正這些場景比較多,因?yàn)閷Ρ群芸欤僮饕埠唵?,兩個查詢一個Set搞定。
Sorted Set:
Sorted set 是排序的 Set,去重但可以排序,寫進(jìn)去的時候給一個分?jǐn)?shù),自動根據(jù)分?jǐn)?shù)排序。
有序集合的使用場景與集合類似,但是set集合不是自動有序的,而Sorted set可以利用分?jǐn)?shù)進(jìn)行成員間的排序,而且是插入時就排序好。所以當(dāng)你需要一個有序且不重復(fù)的集合列表時,就可以選擇Sorted set數(shù)據(jù)結(jié)構(gòu)作為選擇方案。
排行榜:有序集合經(jīng)典使用場景。例如視頻網(wǎng)站需要對用戶上傳的視頻做排行榜,榜單維護(hù)可能是多方面:按照時間、按照播放量、按照獲得的贊數(shù)等。
-
用Sorted Sets來做帶權(quán)重的隊(duì)列,比如普通消息的score為1,重要消息的score為2,然后工作線程可以選擇按score的倒序來獲取工作任務(wù)。讓重要的任務(wù)優(yōu)先執(zhí)行。
微博熱搜榜,就是有個后面的熱度值,前面就是名稱
高級用法:
Bitmap :
位圖是支持按 bit 位來存儲信息,可以用來實(shí)現(xiàn) 布隆過濾器(BloomFilter);
HyperLogLog:
供不精確的去重計(jì)數(shù)功能,比較適合用來做大規(guī)模數(shù)據(jù)的去重統(tǒng)計(jì),例如統(tǒng)計(jì) UV;
Geospatial:
可以用來保存地理位置,并作位置距離計(jì)算或者根據(jù)半徑計(jì)算位置等。有沒有想過用Redis來實(shí)現(xiàn)附近的人?或者計(jì)算最優(yōu)地圖路徑?
這三個其實(shí)也可以算作一種數(shù)據(jù)結(jié)構(gòu),不知道還有多少朋友記得,我在夢開始的地方,Redis基礎(chǔ)中提到過,你如果只知道五種基礎(chǔ)類型那只能拿60分,如果你能講出高級用法,那就覺得你有點(diǎn)東西。
pub/sub:
功能是訂閱發(fā)布功能,可以用作簡單的消息隊(duì)列。
Pipeline:
可以批量執(zhí)行一組指令,一次性返回全部結(jié)果,可以減少頻繁的請求應(yīng)答。
Lua:
Redis 支持提交 Lua 腳本來執(zhí)行一系列的功能。
我在前電商老東家的時候,秒殺場景經(jīng)常使用這個東西,講道理有點(diǎn)香,利用他的原子性。
話說你們想看秒殺的設(shè)計(jì)么?我記得我面試好像每次都問啊,想看的直接點(diǎn)贊后評論秒殺吧。
事務(wù):
最后一個功能是事務(wù),但 Redis 提供的不是嚴(yán)格的事務(wù),Redis 只保證串行執(zhí)行命令,并且能保證全部執(zhí)行,但是執(zhí)行命令失敗時并不會回滾,而是會繼續(xù)執(zhí)行下去。
持久化
Redis 提供了 RDB 和 AOF 兩種持久化方式,RDB 是把內(nèi)存中的數(shù)據(jù)集以快照形式寫入磁盤,實(shí)際操作是通過 fork 子進(jìn)程執(zhí)行,采用二進(jìn)制壓縮存儲;AOF 是以文本日志的形式記錄 Redis 處理的每一個寫入或刪除操作。
RDB 把整個 Redis 的數(shù)據(jù)保存在單一文件中,比較適合用來做災(zāi)備,但缺點(diǎn)是快照保存完成之前如果宕機(jī),這段時間的數(shù)據(jù)將會丟失,另外保存快照時可能導(dǎo)致服務(wù)短時間不可用。
AOF 對日志文件的寫入操作使用的追加模式,有靈活的同步策略,支持每秒同步、每次修改同步和不同步,缺點(diǎn)就是相同規(guī)模的數(shù)據(jù)集,AOF 要大于 RDB,AOF 在運(yùn)行效率上往往會慢于 RDB。
細(xì)節(jié)的點(diǎn)大家去高可用這章看,特別是兩者的優(yōu)缺點(diǎn),以及怎么抉擇。
《吊打面試官》系列-Redis哨兵、持久化、主從、手撕LRU
高可用
來看 Redis 的高可用。Redis 支持主從同步,提供 Cluster 集群部署模式,通過 Sentine l哨兵來監(jiān)控 Redis 主服務(wù)器的狀態(tài)。當(dāng)主掛掉時,在從節(jié)點(diǎn)中根據(jù)一定策略選出新主,并調(diào)整其他從 slaveof 到新主。
選主的策略簡單來說有三個:
- slave 的 priority 設(shè)置的越低,優(yōu)先級越高;
- 同等情況下,slave 復(fù)制的數(shù)據(jù)越多優(yōu)先級越高;
- 相同的條件下 runid 越小越容易被選中。
在 Redis 集群中,sentinel 也會進(jìn)行多實(shí)例部署,sentinel 之間通過 Raft 協(xié)議來保證自身的高可用。
Redis Cluster 使用分片機(jī)制,在內(nèi)部分為 16384 個 slot 插槽,分布在所有 master 節(jié)點(diǎn)上,每個 master 節(jié)點(diǎn)負(fù)責(zé)一部分 slot。數(shù)據(jù)操作時按 key 做 CRC16 來計(jì)算在哪個 slot,由哪個 master 進(jìn)行處理。數(shù)據(jù)的冗余是通過 slave 節(jié)點(diǎn)來保障。
哨兵
哨兵必須用三個實(shí)例去保證自己的健壯性的,哨兵+主從并不能保證數(shù)據(jù)不丟失,但是可以保證集群的高可用。
為啥必須要三個實(shí)例呢?我們先看看兩個哨兵會咋樣。

master宕機(jī)了 s1和s2兩個哨兵只要有一個認(rèn)為你宕機(jī)了就切換了,并且會選舉出一個哨兵去執(zhí)行故障,但是這個時候也需要大多數(shù)哨兵都是運(yùn)行的。
那這樣有啥問題呢?M1宕機(jī)了,S1沒掛那其實(shí)是OK的,但是整個機(jī)器都掛了呢?哨兵就只剩下S2個裸屌了,沒有哨兵去允許故障轉(zhuǎn)移了,雖然另外一個機(jī)器上還有R1,但是故障轉(zhuǎn)移就是不執(zhí)行。
經(jīng)典的哨兵集群是這樣的:

M1所在的機(jī)器掛了,哨兵還有兩個,兩個人一看他不是掛了嘛,那我們就選舉一個出來執(zhí)行故障轉(zhuǎn)移不就好了。
暖男我,小的總結(jié)下哨兵組件的主要功能:
- 集群監(jiān)控:負(fù)責(zé)監(jiān)控 Redis master 和 slave 進(jìn)程是否正常工作。
- 消息通知:如果某個 Redis 實(shí)例有故障,那么哨兵負(fù)責(zé)發(fā)送消息作為報警通知給管理員。
- 故障轉(zhuǎn)移:如果 master node 掛掉了,會自動轉(zhuǎn)移到 slave node 上。
- 配置中心:如果故障轉(zhuǎn)移發(fā)生了,通知 client 客戶端新的 master 地址。
主從
提到這個,就跟我前面提到的數(shù)據(jù)持久化的RDB和AOF有著比密切的關(guān)系了。
我先說下為啥要用主從這樣的架構(gòu)模式,前面提到了單機(jī)QPS是有上限的,而且Redis的特性就是必須支撐讀高并發(fā)的,那你一臺機(jī)器又讀又寫,這誰頂?shù)米“?/strong>,不當(dāng)人??!但是你讓這個master機(jī)器去寫,數(shù)據(jù)同步給別的slave機(jī)器,他們都拿去讀,分發(fā)掉大量的請求那是不是好很多,而且擴(kuò)容的時候還可以輕松實(shí)現(xiàn)水平擴(kuò)容。

你啟動一臺slave 的時候,他會發(fā)送一個psync命令給master ,如果是這個slave第一次連接到master,他會觸發(fā)一個全量復(fù)制。master就會啟動一個線程,生成RDB快照,還會把新的寫請求都緩存在內(nèi)存中,RDB文件生成后,master會將這個RDB發(fā)送給slave的,slave拿到之后做的第一件事情就是寫進(jìn)本地的磁盤,然后加載進(jìn)內(nèi)存,然后master會把內(nèi)存里面緩存的那些新命名都發(fā)給slave。
我發(fā)出來之后來自CSDN的網(wǎng)友:Jian_Shen_Zer 問了個問題:
主從同步的時候,新的slaver進(jìn)來的時候用RDB,那之后的數(shù)據(jù)呢?有新的數(shù)據(jù)進(jìn)入master怎么同步到slaver啊
敖丙答:笨,AOF嘛,增量的就像MySQL的Binlog一樣,把日志增量同步給從服務(wù)就好了
key 失效機(jī)制
Redis 的 key 可以設(shè)置過期時間,過期后 Redis 采用主動和被動結(jié)合的失效機(jī)制,一個是和 MC 一樣在訪問時觸發(fā)被動刪除,另一種是定期的主動刪除。
定期+惰性+內(nèi)存淘汰
緩存常見問題
緩存更新方式
這是決定在使用緩存時就該考慮的問題。
緩存的數(shù)據(jù)在數(shù)據(jù)源發(fā)生變更時需要對緩存進(jìn)行更新,數(shù)據(jù)源可能是 DB,也可能是遠(yuǎn)程服務(wù)。更新的方式可以是主動更新。數(shù)據(jù)源是 DB 時,可以在更新完 DB 后就直接更新緩存。
當(dāng)數(shù)據(jù)源不是 DB 而是其他遠(yuǎn)程服務(wù),可能無法及時主動感知數(shù)據(jù)變更,這種情況下一般會選擇對緩存數(shù)據(jù)設(shè)置失效期,也就是數(shù)據(jù)不一致的最大容忍時間。
這種場景下,可以選擇失效更新,key 不存在或失效時先請求數(shù)據(jù)源獲取最新數(shù)據(jù),然后再次緩存,并更新失效期。
但這樣做有個問題,如果依賴的遠(yuǎn)程服務(wù)在更新時出現(xiàn)異常,則會導(dǎo)致數(shù)據(jù)不可用。改進(jìn)的辦法是異步更新,就是當(dāng)失效時先不清除數(shù)據(jù),繼續(xù)使用舊的數(shù)據(jù),然后由異步線程去執(zhí)行更新任務(wù)。這樣就避免了失效瞬間的空窗期。另外還有一種純異步更新方式,定時對數(shù)據(jù)進(jìn)行分批更新。實(shí)際使用時可以根據(jù)業(yè)務(wù)場景選擇更新方式。
數(shù)據(jù)不一致
第二個問題是數(shù)據(jù)不一致的問題,可以說只要使用緩存,就要考慮如何面對這個問題。緩存不一致產(chǎn)生的原因一般是主動更新失敗,例如更新 DB 后,更新 Redis 因?yàn)榫W(wǎng)絡(luò)原因請求超時;或者是異步更新失敗導(dǎo)致。
解決的辦法是,如果服務(wù)對耗時不是特別敏感可以增加重試;如果服務(wù)對耗時敏感可以通過異步補(bǔ)償任務(wù)來處理失敗的更新,或者短期的數(shù)據(jù)不一致不會影響業(yè)務(wù),那么只要下次更新時可以成功,能保證最終一致性就可以。
緩存穿透
緩存穿透。產(chǎn)生這個問題的原因可能是外部的惡意攻擊,例如,對用戶信息進(jìn)行了緩存,但惡意攻擊者使用不存在的用戶id頻繁請求接口,導(dǎo)致查詢緩存不命中,然后穿透 DB 查詢依然不命中。這時會有大量請求穿透緩存訪問到 DB。
解決的辦法如下。
- 對不存在的用戶,在緩存中保存一個空對象進(jìn)行標(biāo)記,防止相同 ID 再次訪問 DB。不過有時這個方法并不能很好解決問題,可能導(dǎo)致緩存中存儲大量無用數(shù)據(jù)。
- 使用 BloomFilter 過濾器,BloomFilter 的特點(diǎn)是存在性檢測,如果 BloomFilter 中不存在,那么數(shù)據(jù)一定不存在;如果 BloomFilter 中存在,實(shí)際數(shù)據(jù)也有可能會不存在。非常適合解決這類的問題。
緩存擊穿
緩存擊穿,就是某個熱點(diǎn)數(shù)據(jù)失效時,大量針對這個數(shù)據(jù)的請求會穿透到數(shù)據(jù)源。
解決這個問題有如下辦法。
- 可以使用互斥鎖更新,保證同一個進(jìn)程中針對同一個數(shù)據(jù)不會并發(fā)請求到 DB,減小 DB 壓力。
- 使用隨機(jī)退避方式,失效時隨機(jī) sleep 一個很短的時間,再次查詢,如果失敗再執(zhí)行更新。
- 針對多個熱點(diǎn) key 同時失效的問題,可以在緩存時使用固定時間加上一個小的隨機(jī)數(shù),避免大量熱點(diǎn) key 同一時刻失效。
緩存雪崩
緩存雪崩,產(chǎn)生的原因是緩存掛掉,這時所有的請求都會穿透到 DB。
解決方法:
- 使用快速失敗的熔斷策略,減少 DB 瞬間壓力;
- 使用主從模式和集群模式來盡量保證緩存服務(wù)的高可用。
實(shí)際場景中,這兩種方法會結(jié)合使用。
老朋友都知道為啥我沒有大篇幅介紹這個幾個點(diǎn)了吧,我在之前的文章實(shí)在是寫得太詳細(xì)了,忍不住點(diǎn)贊那種,我這里就不做重復(fù)拷貝了。
- 《吊打面試官》系列-Redis基礎(chǔ)
- 《吊打面試官》系列-緩存雪崩、擊穿、穿透
- 《吊打面試官》系列-Redis哨兵、持久化、主從、手撕LRU
- 《吊打面試官》系列-Redis終章凜冬將至、FPX新王登基
考點(diǎn)與加分項(xiàng)
拿筆記一下!

考點(diǎn)
面試的時候問你緩存,主要是考察緩存特性的理解,對 MC、Redis 的特點(diǎn)和使用方式的掌握。
- 要知道緩存的使用場景,不同類型緩存的使用方式,例如:
- 對 DB 熱點(diǎn)數(shù)據(jù)進(jìn)行緩存減少 DB 壓力;對依賴的服務(wù)進(jìn)行緩存,提高并發(fā)性能;
- 單純 K-V 緩存的場景可以使用 MC,而需要緩存 list、set 等特殊數(shù)據(jù)格式,可以使用 Redis;
- 需要緩存一個用戶最近播放視頻的列表可以使用 Redis 的 list 來保存、需要計(jì)算排行榜數(shù)據(jù)時,可以使用 Redis 的 zset 結(jié)構(gòu)來保存。
要了解 MC 和 Redis 的常用命令,例如原子增減、對不同數(shù)據(jù)結(jié)構(gòu)進(jìn)行操作的命令等。
了解 MC 和 Redis 在內(nèi)存中的存儲結(jié)構(gòu),這對評估使用容量會很有幫助。
了解 MC 和 Redis 的數(shù)據(jù)失效方式和剔除策略,比如主動觸發(fā)的定期剔除和被動觸發(fā)延期剔除
要理解 Redis 的持久化、主從同步與 Cluster 部署的原理,比如 RDB 和 AOF 的實(shí)現(xiàn)方式與區(qū)別。
要知道緩存穿透、擊穿、雪崩分別的異同點(diǎn)以及解決方案。
不管你有沒有電商經(jīng)驗(yàn)我覺得你都應(yīng)該知道秒殺的具體實(shí)現(xiàn),以及細(xì)節(jié)點(diǎn)。
-
........
歡迎去GitHub補(bǔ)充
加分項(xiàng)
如果想要在面試中獲得更好的表現(xiàn),還應(yīng)了解下面這些加分項(xiàng)。
是要結(jié)合實(shí)際應(yīng)用場景來介紹緩存的使用。例如調(diào)用后端服務(wù)接口獲取信息時,可以使用本地+遠(yuǎn)程的多級緩存;對于動態(tài)排行榜類的場景可以考慮通過 Redis 的 Sorted set 來實(shí)現(xiàn)等等。
最好你有過分布式緩存設(shè)計(jì)和使用經(jīng)驗(yàn),例如項(xiàng)目中在什么場景使用過 Redis,使用了什么數(shù)據(jù)結(jié)構(gòu),解決哪類的問題;使用 MC 時根據(jù)預(yù)估值大小調(diào)整 McSlab 分配參數(shù)等等。
最好可以了解緩存使用中可能產(chǎn)生的問題。比如 Redis 是單線程處理請求,應(yīng)盡量避免耗時較高的單個請求任務(wù),防止相互影響;Redis 服務(wù)應(yīng)避免和其他 CPU 密集型的進(jìn)程部署在同一機(jī)器;或者禁用 Swap 內(nèi)存交換,防止 Redis 的緩存數(shù)據(jù)交換到硬盤上,影響性能。再比如前面提到的 MC 鈣化問題等等。
要了解 Redis 的典型應(yīng)用場景,例如,使用 Redis 來實(shí)現(xiàn)分布式鎖;使用 Bitmap 來實(shí)現(xiàn) BloomFilter,使用 HyperLogLog 來進(jìn)行 UV 統(tǒng)計(jì)等等。
知道 Redis4.0、5.0 中的新特性,例如支持多播的可持久化消息隊(duì)列 Stream;通過 Module 系統(tǒng)來進(jìn)行定制功能擴(kuò)展等等。
........
還是那句話歡迎去GitHub補(bǔ)充。
總結(jié)
這次是對我Redis系列的總結(jié),這應(yīng)該是Redis相關(guān)的最后一篇文章了,其實(shí)四篇看下來的小伙伴很多都從一知半解到了一臉懵逼,哈哈開個玩笑。
我覺得我的方式應(yīng)該還好,大部分小伙伴還是比較能理解的,這篇之后我就不會寫Redis相關(guān)的文章了(秒殺看大家想看的熱度吧),有啥問題可以微信找我,下個系列寫啥?
大家不用急,下個系列前我會發(fā)個有意思的文章,是我在公司代碼創(chuàng)意大賽拿獎的文章,我覺得還是有點(diǎn)東西,我忍不住分享一下,順便就在那期發(fā)起投票吧哈哈。
我看到很多小伙伴都有評論說想看別的,大概搜集了一下,還沒留言的這期趕緊喲:
掘金
愚辛 :想看計(jì)算機(jī)基礎(chǔ),網(wǎng)絡(luò)和操作系統(tǒng)那些(FPX牛脾)
cherish君:講講dubbo經(jīng)常遇到的面試題目,太多人喜歡問dubbo??
Java架構(gòu)養(yǎng)成記:真的很香啊,下一期講Dubbbo(重點(diǎn)SPI)然后講MQ好嗎
CSDN
小殿下:看完了所有的redis篇 希望可以出ssm
博客園
程然:Dubbo Dubbo
開源中國
linshi2019:這期明顯是趕工之作啊
敖丙:這條我回一下,鞭策我,我很喜歡,不過說實(shí)話還是希望大家理解下,我雙十一熬夜三天了,現(xiàn)在給你們寫的時候也是值班回家2點(diǎn)左右了,我一天吃飯工作時間肯定是固定的,想寫點(diǎn)東西就只有擠出睡覺時間了,這種產(chǎn)出肯定沒周末全情投入寫的來的質(zhì)量高。
其實(shí)第一期看過來的小伙伴應(yīng)該也知道,我在排版,還有很多文案,配圖其實(shí)我一直都有在改進(jìn)的,光是名詞高亮我都要弄很久,因?yàn)榕麓蠹铱磫我坏暮诎咨{(diào)枯燥。
我是真的用心在搞,還是希望大家支持下理解下。
知乎、簡書、思否、慕課手記沒人看不知道為啥,懂行的老鐵可以跟我說一下。
我只想說你們想看的肯定都在我開頭和GITHub那個圖里吧,問題不大,后面都會寫的。
鳴謝
最后感謝下,新浪微博的技術(shù)專家張雷。
他于2013年加入新浪微博,作為核心技術(shù)人員參與了微博服務(wù)化、混合云等多個重點(diǎn)項(xiàng)目,是微博開源的RPC框架Motan的技術(shù)負(fù)責(zé)人,同時也負(fù)責(zé)微博的Service Mesh方案的研發(fā)與推廣,專注于高可用架構(gòu)及服務(wù)中間件開發(fā)方向。
他負(fù)責(zé)的Motan框架每天承載著萬億級別的請求調(diào)用,是微博平臺服務(wù)化的基石,每次的突發(fā)熱點(diǎn)事件、每次的春晚流量高峰,都離不開Motan框架的支撐與保障。此外,他也多次應(yīng)邀在ArchSummit、WOT、GIAC技術(shù)峰會做技術(shù)分享。
感謝他對文章部分文案提供的支持和思路。
點(diǎn)關(guān)注,不迷路
好了各位,以上就是這篇文章的全部內(nèi)容了,能看到這里的人呀,都是人才。
我后面會每周都更新幾篇一線互聯(lián)網(wǎng)大廠面試和常用技術(shù)棧相關(guān)的文章,非常感謝人才們能看到這里,如果這個文章寫得還不錯,覺得「敖丙」我有點(diǎn)東西的話 求點(diǎn)贊?? 求關(guān)注?? 求分享?? 對暖男我來說真的 非常有用!?。?/p>
創(chuàng)作不易,各位的支持和認(rèn)可,就是我創(chuàng)作的最大動力,我們下篇文章見!
敖丙 | 文 【原創(chuàng)】
如果本篇博客有任何錯誤,請批評指教,不勝感激 !
文章每周持續(xù)更新,可以微信搜索「 三太子敖丙 」第一時間閱讀和催更(比博客早一到兩篇喲),本文 GitHub https://github.com/JavaFamily 已經(jīng)收錄,有一線大廠面試點(diǎn)思維導(dǎo)圖,也整理了很多我的文檔,歡迎Star和完善,大家面試可以參照考點(diǎn)復(fù)習(xí),希望我們一起有點(diǎn)東西。