Redis的那些常見面試題總結(附答案解析)

面了6家大廠,把問爛了的redis常見面試題(附答案解析)總結一下分享給大家。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有所幫助。

Redis的那些常見面試題總結(附答案解析)

【相關推薦:Redis視頻教程

緩存知識點

Redis的那些常見面試題總結(附答案解析)

緩存有哪些類型?

緩存是高并發場景下提高熱點數據訪問性能的一個有效手段,在開發項目時會經常使用到。

緩存的類型分為:本地緩存分布式緩存多級緩存

本地緩存:

本地緩存就是在進程的內存中進行緩存,比如我們的 jvm 中,可以用 LRUmap 來實現,也可以使用 Ehcache 這樣的工具來實現。

本地緩存是內存訪問,沒有遠程交互開銷,性能最好,但是受限于單機容量,一般緩存較小且無法擴展。

分布式緩存:

分布式緩存可以很好得解決這個問題。

分布式緩存一般都具有良好的水平擴展能力,對較大數據量的場景也能應付自如。缺點就是需要進行遠程請求,性能不如本地緩存。

多級緩存:

為了平衡這種情況,實際業務中一般采用多級緩存,本地緩存只保存訪問頻率最高的部分熱點數據,其他的熱點數據放在分布式緩存中。

在目前的一線大廠中,這也是最常用的緩存方案,單考單一的緩存方案往往難以撐住很多高并發的場景。

淘汰策略

不管是本地緩存還是分布式緩存,為了保證較高性能,都是使用內存來保存數據,由于成本和內存限制,當存儲的數據超過緩存容量時,需要對緩存的數據進行剔除。

一般的剔除策略有 FIFO 淘汰最早數據、LRU 剔除最近最少使用、和 LFU 剔除最近使用頻率最低的數據幾種策略。

  • noeviction:返回錯誤當內存限制達到并且客戶端嘗試執行會讓更多內存被使用的命令(大部分的寫入指令,但DEL和幾個例外)

  • allkeys-lru: 嘗試回收最少使用的鍵(LRU),使得新添加的數據有空間存放。

  • volatile-lru: 嘗試回收最少使用的鍵(LRU),但僅限于在過期集合的鍵,使得新添加的數據有空間存放。

  • allkeys-random: 回收隨機的鍵使得新添加的數據有空間存放。

  • volatile-random: 回收隨機的鍵使得新添加的數據有空間存放,但僅限于在過期集合的鍵。

  • volatile-ttl: 回收在過期集合的鍵,并且優先回收存活時間(TTL)較短的鍵,使得新添加的數據有空間存放。

    如果沒有鍵滿足回收的前提條件的話,策略volatile-lru, volatile-random以及volatile-ttl就和noeviction 差不多了。

其實在大家熟悉的LinkedHashMap中也實現了Lru算法的,實現如下:

Redis的那些常見面試題總結(附答案解析)

當容量超過100時,開始執行LRU策略:將最近最少未使用的 TimeoutInfoHolder 對象 evict 掉。

真實面試中會讓你寫LUR算法,你可別搞原始的那個,那真TM多,寫不完的,你要么懟上面這個,要么懟下面這個,找一個數據結構實現下Java版本的LRU還是比較容易的,知道啥原理就好了。

Redis的那些常見面試題總結(附答案解析)

memcache

注意后面會把 Memcache 簡稱為 MC。

先來看看 MC 的特點:

  • MC 處理請求時使用線程異步 IO 的方式,可以合理利用 CPU 多核的優勢,性能非常優秀;
  • MC 功能簡單,使用內存存儲數據;
  • MC 的內存結構以及鈣化問題我就不細說了,大家可以查看官網了解下;
  • MC 對緩存的數據可以設置失效期,過期后的數據會被清除;
  • 失效的策略采用延遲失效,就是當再次使用數據時檢查是否失效;
  • 當容量存滿時,會對緩存中的數據進行剔除,剔除時除了會對過期 key 進行清理,還會按 LRU 策略對數據進行剔除。

另外,使用 MC 有一些限制,這些限制在現在的互聯網場景下很致命,成為大家選擇redismongodb的重要原因:

  • key 不能超過 250 個字節;
  • value 不能超過 1M 字節;
  • key 的最大失效時間是 30 天;
  • 只支持 K-V 結構,不提供持久化和主從同步功能。

Redis

先簡單說一下 Redis 的特點,方便和 MC 比較。

  • 與 MC 不同的是,Redis 采用單線程模式處理請求。這樣做的原因有 2 個:一個是因為采用了非阻塞的異步事件處理機制;另一個是緩存數據都是內存操作 IO 時間不會太長,單線程可以避免線程上下文切換產生的代價。
  • Redis 支持持久化,所以 Redis 不僅僅可以用作緩存,也可以用作 nosql 數據庫
  • 相比 MC,Redis 還有一個非常大的優勢,就是除了 K-V 之外,還支持多種數據格式,例如 list、set、sorted set、hash 等。
  • Redis 提供主從同步機制,以及 Cluster 集群部署能力,能夠提供高可用服務。

詳解 Redis

Redis 的知識點結構如下圖所示。

Redis的那些常見面試題總結(附答案解析)

功能

來看 Redis 提供的功能有哪些吧!

我們先看基礎類型:

String

String 類型是 Redis 中最常使用的類型,內部的實現是通過 SDS(Simple Dynamic String )來存儲的。SDS 類似于 Java 中的 ArrayList,可以通過預分配冗余空間的方式來減少內存的頻繁分配。

這是最簡單的類型,就是普通的 set 和 get,做簡單的 KV 緩存。

但是真實的開發環境中,很多仔可能會把很多比較復雜的結構也統一轉成String去存儲使用,比如有的仔他就喜歡把對象或者List轉換為JSONString進行存儲,拿出來再反序列話啥的。

我在這里就不討論這樣做的對錯了,但是我還是希望大家能在最合適的場景使用最合適的數據結構,對象找不到最合適的但是類型可以選最合適的嘛,之后別人接手你的代碼一看這么規范,誒這小伙子有點東西呀,看到你啥都是用的String垃圾!

好了這些都是題外話了,道理還是希望大家記在心里,習慣成自然嘛,小習慣成就你。

String的實際應用場景比較廣泛的有:

  • 緩存功能:String字符串是最常用的數據類型,不僅僅是Redis,各個語言都是最基本類型,因此,利用Redis作為緩存,配合其它數據庫作為存儲層,利用Redis支持高并發的特點,可以大大加快系統的讀寫速度、以及降低后端數據庫的壓力。

  • 計數器:許多系統都會使用Redis作為系統的實時計數器,可以快速實現計數和查詢的功能。而且最終的數據結果可以按照特定的時間落地到數據庫或者其它存儲介質當中進行永久保存。

  • 共享用戶Session用戶重新刷新一次界面,可能需要訪問一下數據進行重新登錄,或者訪問頁面緩存Cookie,但是可以利用Redis將用戶的Session集中管理,在這種模式只需要保證Redis的高可用,每次用戶Session的更新和獲取都可以快速完成。大大提高效率。

Hash:

這個是類似 Map 的一種結構,這個一般就是可以將結構化的數據,比如一個對象(前提是這個對象沒嵌套其他的對象)給緩存在 Redis 里,然后每次讀寫緩存的時候,可以就操作 Hash 里的某個字段

但是這個的場景其實還是多少單一了一些,因為現在很多對象都是比較復雜的,比如你的商品對象可能里面就包含了很多屬性,其中也有對象。我自己使用的場景用得不是那么多。

List:

List 是有序列表,這個還是可以玩兒出很多花樣的。

比如可以通過 List 存儲一些列表型的數據結構,類似粉絲列表、文章的評論列表之類的東西。

比如可以通過 lrange 命令,讀取某個閉區間內的元素,可以基于 List 實現分頁查詢,這個是很棒的一個功能,基于 Redis 實現簡單的高性能分頁,可以做類似微博那種下拉不斷分頁的東西,性能高,就一頁一頁走。

比如可以搞個簡單的消息隊列,從 List 頭懟進去,從 List 屁股那里弄出來。

List本身就是我們在開發過程中比較常用的數據結構了,熱點數據更不用說了。

  • 消息隊列:Redis的鏈表結構,可以輕松實現阻塞隊列,可以使用左進右出的命令組成來完成隊列的設計。比如:數據的生產者可以通過Lpush命令從左邊插入數據,多個數據消費者,可以使用BRpop命令阻塞的“搶”列表尾部的數據。

  • 文章列表或者數據分頁展示的應用。

    比如,我們常用的博客網站的文章列表,當用戶量越來越多時,而且每一個用戶都有自己的文章列表,而且當文章多時,都需要分頁展示,這時可以考慮使用Redis的列表,列表不但有序同時還支持按照范圍內獲取元素,可以完美解決分頁查詢功能。大大提高查詢效率。

Set:

Set 是無序集合,會自動去重的那種。

直接基于 Set 將系統里需要去重的數據扔進去,自動就給去重了,如果你需要對一些數據進行快速的全局去重,你當然也可以基于 JVM 內存里的 HashSet 進行去重,但是如果你的某個系統部署在多臺機器上呢?得基于Redis進行全局的 Set 去重。

可以基于 Set 玩兒交集、并集、差集的操作,比如交集吧,我們可以把兩個人的好友列表整一個交集,看看倆人的共同好友是誰?對吧。

反正這些場景比較多,因為對比很快,操作也簡單,兩個查詢一個Set搞定。

Sorted Set:

Sorted set 是排序的 Set,去重但可以排序,寫進去的時候給一個分數,自動根據分數排序。

有序集合的使用場景與集合類似,但是set集合不是自動有序的,而Sorted set可以利用分數進行成員間的排序,而且是插入時就排序好。所以當你需要一個有序且不重復的集合列表時,就可以選擇Sorted set數據結構作為選擇方案。

  • 排行榜:有序集合經典使用場景。例如視頻網站需要對用戶上傳的視頻做排行榜,榜單維護可能是多方面:按照時間、按照播放量、按照獲得的贊數等。

  • Sorted Sets來做帶權重的隊列,比如普通消息的score為1,重要消息的score為2,然后工作線程可以選擇按score的倒序來獲取工作任務。讓重要的任務優先執行。

    微博熱搜榜,就是有個后面的熱度值,前面就是名稱

高級用法:

Bitmap :

位圖是支持按 bit 位來存儲信息,可以用來實現 布隆過濾器(BloomFilter)

HyperLogLog:

供不精確的去重計數功能,比較適合用來做大規模數據的去重統計,例如統計 UV;

Geospatial:

可以用來保存地理位置,并作位置距離計算或者根據半徑計算位置等。有沒有想過用Redis來實現附近的人?或者計算最優地圖路徑?

這三個其實也可以算作一種數據結構,不知道還有多少朋友記得,我在夢開始的地方,Redis基礎中提到過,你如果只知道五種基礎類型那只能拿60分,如果你能講出高級用法,那就覺得你有點東西

pub/sub:

功能是訂閱發布功能,可以用作簡單的消息隊列。

Pipeline:

可以批量執行一組指令,一次性返回全部結果,可以減少頻繁的請求應答。

lua

Redis 支持提交 Lua 腳本來執行一系列的功能。

我在前電商老東家的時候,秒殺場景經常使用這個東西,講道理有點香,利用他的原子性。

話說你們想看秒殺的設計么?我記得我面試好像每次都問啊,想看的直接點贊后評論秒殺吧。

事務:

最后一個功能是事務,但 Redis 提供的不是嚴格的事務,Redis 只保證串行執行命令,并且能保證全部執行,但是執行命令失敗時并不會回滾,而是會繼續執行下去。

持久化

Redis 提供了 RDB 和 AOF 兩種持久化方式,RDB 是把內存中的數據集以快照形式寫入磁盤,實際操作是通過 fork 子進程執行,采用二進制壓縮存儲;AOF 是以文本日志的形式記錄 Redis 處理的每一個寫入或刪除操作。

RDB 把整個 Redis 的數據保存在單一文件中,比較適合用來做災備,但缺點是快照保存完成之前如果宕機,這段時間的數據將會丟失,另外保存快照時可能導致服務短時間不可用。

AOF 對日志文件的寫入操作使用的追加模式,有靈活的同步策略,支持每秒同步、每次修改同步和不同步,缺點就是相同規模的數據集,AOF 要大于 RDB,AOF 在運行效率上往往會慢于 RDB。

細節的點大家去高可用這章看,特別是兩者的優缺點,以及怎么抉擇。

《吊打面試官》系列-Redis哨兵、持久化、主從、手撕LRU

高可用

來看 Redis 的高可用。Redis 支持主從同步,提供 Cluster 集群部署模式,通過 Sentine l哨兵來監控 Redis 主服務器的狀態。當主掛掉時,在從節點中根據一定策略選出新主,并調整其他從 slaveof 到新主。

選主的策略簡單來說有三個:

  • slave 的 priority 設置的越低,優先級越高;
  • 同等情況下,slave 復制的數據越多優先級越高;
  • 相同的條件下 runid 越小越容易被選中。

在 Redis 集群中,sentinel 也會進行多實例部署,sentinel 之間通過 Raft 協議來保證自身的高可用。

Redis Cluster 使用分片機制,在內部分為 16384 個 slot 插槽,分布在所有 master 節點上,每個 master 節點負責一部分 slot。數據操作時按 key 做 CRC16 來計算在哪個 slot,由哪個 master 進行處理。數據的冗余是通過 slave 節點來保障。

哨兵

哨兵必須用三個實例去保證自己的健壯性的,哨兵+主從并不能保證數據不丟失,但是可以保證集群的高可用

為啥必須要三個實例呢?我們先看看兩個哨兵會咋樣。

Redis的那些常見面試題總結(附答案解析)

master宕機了 s1和s2兩個哨兵只要有一個認為你宕機了就切換了,并且會選舉出一個哨兵去執行故障,但是這個時候也需要大多數哨兵都是運行的。

那這樣有啥問題呢?M1宕機了,S1沒掛那其實是OK的,但是整個機器都掛了呢?哨兵就只剩下S2個裸屌了,沒有哨兵去允許故障轉移了,雖然另外一個機器上還有R1,但是故障轉移就是不執行。

經典的哨兵集群是這樣的:

Redis的那些常見面試題總結(附答案解析)

M1所在的機器掛了,哨兵還有兩個,兩個人一看他不是掛了嘛,那我們就選舉一個出來執行故障轉移不就好了。

暖男我,小的總結下哨兵組件的主要功能:

  • 集群監控:負責監控 Redis master 和 slave 進程是否正常工作。
  • 消息通知:如果某個 Redis 實例有故障,那么哨兵負責發送消息作為報警通知給管理員。
  • 故障轉移:如果 master node 掛掉了,會自動轉移到 slave node 上。
  • 配置中心:如果故障轉移發生了,通知 client 客戶端新的 master 地址。

主從

提到這個,就跟我前面提到的數據持久化的RDBAOF有著比密切的關系了。

我先說下為啥要用主從這樣的架構模式,前面提到了單機QPS是有上限的,而且Redis的特性就是必須支撐讀高并發的,那你一臺機器又讀又寫,這誰頂得住啊,不當人啊!但是你讓這個master機器去寫,數據同步給別的slave機器,他們都拿去讀,分發掉大量的請求那是不是好很多,而且擴容的時候還可以輕松實現水平擴容。

Redis的那些常見面試題總結(附答案解析)

你啟動一臺slave 的時候,他會發送一個psync命令給master ,如果是這個slave第一次連接到master,他會觸發一個全量復制。master就會啟動一個線程,生成RDB快照,還會把新的寫請求都緩存在內存中,RDB文件生成后,master會將這個RDB發送給slave的,slave拿到之后做的第一件事情就是寫進本地的磁盤,然后加載進內存,然后master會把內存里面緩存的那些新命名都發給slave。

我發出來之后來自CSDN的網友:Jian_Shen_Zer 問了個問題:

主從同步的時候,新的slaver進來的時候用RDB,那之后的數據呢?有新的數據進入master怎么同步到slaver啊

敖丙答:笨,AOF嘛,增量的就像mysqlBinlog一樣,把日志增量同步給從服務就好了

key 失效機制

Redis 的 key 可以設置過期時間,過期后 Redis 采用主動和被動結合的失效機制,一個是和 MC 一樣在訪問時觸發被動刪除,另一種是定期的主動刪除。

定期+惰性+內存淘汰

緩存常見問題

緩存更新方式

這是決定在使用緩存時就該考慮的問題。

緩存的數據在數據源發生變更時需要對緩存進行更新,數據源可能是 DB,也可能是遠程服務。更新的方式可以是主動更新。數據源是 DB 時,可以在更新完 DB 后就直接更新緩存。

當數據源不是 DB 而是其他遠程服務,可能無法及時主動感知數據變更,這種情況下一般會選擇對緩存數據設置失效期,也就是數據不一致的最大容忍時間。

這種場景下,可以選擇失效更新,key 不存在或失效時先請求數據源獲取最新數據,然后再次緩存,并更新失效期。

但這樣做有個問題,如果依賴的遠程服務在更新時出現異常,則會導致數據不可用。改進的辦法是異步更新,就是當失效時先不清除數據,繼續使用舊的數據,然后由異步線程去執行更新任務。這樣就避免了失效瞬間的空窗期。另外還有一種純異步更新方式,定時對數據進行分批更新。實際使用時可以根據業務場景選擇更新方式。

數據不一致

第二個問題是數據不一致的問題,可以說只要使用緩存,就要考慮如何面對這個問題。緩存不一致產生的原因一般是主動更新失敗,例如更新 DB 后,更新 Redis 因為網絡原因請求超時;或者是異步更新失敗導致。

解決的辦法是,如果服務對耗時不是特別敏感可以增加重試;如果服務對耗時敏感可以通過異步補償任務來處理失敗的更新,或者短期的數據不一致不會影響業務,那么只要下次更新時可以成功,能保證最終一致性就可以。

緩存穿透

緩存穿透。產生這個問題的原因可能是外部的惡意攻擊,例如,對用戶信息進行了緩存,但惡意攻擊者使用不存在的用戶id頻繁請求接口,導致查詢緩存不命中,然后穿透 DB 查詢依然不命中。這時會有大量請求穿透緩存訪問到 DB。

解決的辦法如下。

  • 對不存在的用戶,在緩存中保存一個空對象進行標記,防止相同 ID 再次訪問 DB。不過有時這個方法并不能很好解決問題,可能導致緩存中存儲大量無用數據。

  • 使用 BloomFilter 過濾器,BloomFilter 的特點是存在性檢測,如果 BloomFilter 中不存在,那么數據一定不存在;如果 BloomFilter 中存在,實際數據也有可能會不存在。非常適合解決這類的問題。

緩存擊穿

緩存擊穿,就是某個熱點數據失效時,大量針對這個數據的請求會穿透到數據源。

解決這個問題有如下辦法。

  • 可以使用互斥鎖更新,保證同一個進程中針對同一個數據不會并發請求到 DB,減小 DB 壓力。

  • 使用隨機退避方式,失效時隨機 sleep 一個很短的時間,再次查詢,如果失敗再執行更新。

  • 針對多個熱點 key 同時失效的問題,可以在緩存時使用固定時間加上一個小的隨機數,避免大量熱點 key 同一時刻失效。

緩存雪崩

緩存雪崩,產生的原因是緩存掛掉,這時所有的請求都會穿透到 DB。

解決方法:

  • 使用快速失敗的熔斷策略,減少 DB 瞬間壓力;

  • 使用主從模式和集群模式來盡量保證緩存服務的高可用。

實際場景中,這兩種方法會結合使用。

老朋友都知道為啥我沒有大篇幅介紹這個幾個點了吧,我在之前的文章實在是寫得太詳細了,忍不住點贊那種,我這里就不做重復拷貝了。

  • 《吊打面試官》系列-Redis基礎
  • 《吊打面試官》系列-緩存雪崩、擊穿、穿透
  • 《吊打面試官》系列-Redis哨兵、持久化、主從、手撕LRU
  • 《吊打面試官》系列-Redis終章-凜冬將至、FPX-新王登基

考點與加分項

拿筆記一下!

Redis的那些常見面試題總結(附答案解析)

考點

面試的時候問你緩存,主要是考察緩存特性的理解,對 MCRedis 的特點和使用方式的掌握。

  • 要知道緩存的使用場景,不同類型緩存的使用方式,例如:
  • – 對 DB 熱點數據進行緩存減少 DB 壓力;對依賴的服務進行緩存,提高并發性能;

  • – 單純 K-V 緩存的場景可以使用 MC,而需要緩存 list、set 等特殊數據格式,可以使用 Redis

  • – 需要緩存一個用戶最近播放視頻的列表可以使用 Redis 的 list 來保存、需要計算排行榜數據時,可以使用 Redis 的 zset 結構來保存。

  • 要了解 MC 和 Redis 的常用命令,例如原子增減、對不同數據結構進行操作的命令等。

  • 了解 MC 和 Redis 在內存中的存儲結構,這對評估使用容量會很有幫助。

  • 了解 MC 和 Redis 的數據失效方式和剔除策略,比如主動觸發的定期剔除和被動觸發延期剔除

  • 要理解 Redis 的持久化、主從同步與 Cluster 部署的原理,比如 RDBAOF 的實現方式與區別。

  • 要知道緩存穿透、擊穿、雪崩分別的異同點以及解決方案。

  • 不管你有沒有電商經驗我覺得你都應該知道秒殺的具體實現,以及細節點。

  • ……..

加分項

如果想要在面試中獲得更好的表現,還應了解下面這些加分項。

  • 是要結合實際應用場景來介紹緩存的使用。例如調用后端服務接口獲取信息時,可以使用本地+遠程的多級緩存;對于動態排行榜類的場景可以考慮通過 RedisSorted set 來實現等等。

  • 最好你有過分布式緩存設計和使用經驗,例如項目中在什么場景使用過 Redis,使用了什么數據結構,解決哪類的問題;使用 MC 時根據預估值大小調整 McSlab 分配參數等等。

  • 最好可以了解緩存使用中可能產生的問題。比如 Redis 是單線程處理請求,應盡量避免耗時較高的單個請求任務,防止相互影響;Redis 服務應避免和其他 CPU 密集型的進程部署在同一機器;或者禁用 Swap 內存交換,防止 Redis 的緩存數據交換到硬盤上,影響性能。再比如前面提到的 MC 鈣化問題等等。

  • 要了解 Redis 的典型應用場景,例如,使用 Redis 來實現分布式鎖;使用 Bitmap 來實現 BloomFilter,使用 HyperLogLog 來進行 UV 統計等等。

  • 知道 Redis4.0、5.0 中的新特性,例如支持多播的可持久化消息隊列 Stream;通過 Module 系統來進行定制功能擴展等等。

  • ……..

更多編程相關知識,請訪問:Redis視頻教程!!

? 版權聲明
THE END
喜歡就支持一下吧
點贊9 分享