Redis 面經

1 緩存有哪些類型?

  1. 本地緩存:本地緩存就是在進程的內存中進行緩存。本地緩存是內存訪問,沒有遠程交互開銷,性能最好,但是受限於單機容量,一般緩存較小且無法擴展。
  2. 分佈式緩存:分佈式緩存一般都具有良好的水平擴展能力,對較大數據量的場景也能應付自如。缺點就是需要進行遠程請求,性能不如本地緩存。
  3. 多級緩存:爲了平衡這種情況,實際業務中一般採用多級緩存,本地緩存只保存訪問頻率最高的部分熱點數據,其他的熱點數據放在分佈式緩存中。

2 爲什麼我們需要使用 Redis?

傳統的關係型數據庫如 Mysql 已經不能適用所有的場景了,比如秒殺的庫存扣減,APP首頁的訪問流量高峯等等,都很容易把數據庫打崩,所以引入了緩存中間件 Redis。

3 Redis 爲什麼這麼快?

  1. 完全基於內存,絕大部分請求是純粹的內存操作,非常快速
  2. 數據結構簡單,對數據操作也簡單
  3. 採用單線程,避免了不必要的上下文切換和競爭條件,也不存在多進程或者多線程導致的切換而消耗 CPU,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有因爲可能出現死鎖而導致的性能消耗
  4. 使用多路 I/O 複用模型,非阻塞 IO

4 Memcached 的優點與缺點

Memcache 的優點如下:

  1. Memcached 處理請求時使用多線程異步 IO 的方式,可以合理利用 CPU 多核的優勢,性能非常優秀;
  2. Memcached 功能簡單,使用內存存儲數據;
  3. Memcached 對緩存的數據可以設置失效期,過期後的數據會被清除;
  4. 失效的策略採用延遲失效,就是當再次使用數據時檢查是否失效;
  5. 當容量存滿時,會對緩存中的數據進行剔除,剔除時除了會對過期 key 進行清理,還會按 LRU 策略對數據進行剔除。

Memcache 的缺點如下:

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

5 Redis 和 Memcached 的區別?

  1. 可靠性:Redis 支持持久化,有快照和 AOF 兩種方式,而 Memcached 是純的內存存儲,不支持持久化的。
  2. 數據結構:Memcached 僅僅支持 string,而 Redis 支持 string,list,set,sorted set,hash。
  3. value 大小:Redis 最大可以達到 1GB,而 Memcached 只有 1MB。
  4. 線程:Memcached 使用多線程,主線程 listen,多個 worker 子線程執行讀寫,可能會出現鎖衝突。Redis 是單線程的,這樣雖然不用考慮鎖對插入修改數據造成的時間的影響,但是無法利用多核提高整體的吞吐量,只能選擇多開 Redis 來解決。
  5. 集羣:Redis 原生支持集羣模式,在 redis3.x 版本中,便能支持 Cluster 模式,而 Memcached 沒有原生的集羣模式,需要依靠客戶端來實現往集羣中分片寫入數據。
  6. 性能:由於 Redis 只使用單核,而 Memcached 可以使用多核,所以平均每一個核上 Redis 在存儲小數據時比 Memcached 性能更高。而在 100k 以上的數據中,Memcached 性能要高於 Redis,雖然 Redis 最近也在存儲大數據的性能上進行優化,但是比起 Remcached,還是稍有遜色。

6 Redis 具有的數據結構

  1. String:字符串
  2. Hash:字典
  3. List:列表
  4. Set:集合
  5. SortedSet:有序集合

7 布隆過濾器 (BloomFilter)

Redis-避免緩存穿透的利器之BloomFilter

8 Redis 分佈式鎖

分佈式鎖的實現方式

9 假如 Redis 裏面有1億個 key,其中有10w個 key 是以某個固定的已知的前綴開頭的,如何將它們全部找出來?

使用 keys 指令可以掃出指定模式的 key 列表。

10 如果這個 Redis 正在給線上的業務提供服務,那使用 keys 指令會有什麼問題?

Redis 是單線程的。keys 指令會導致線程阻塞一段時間,線上服務會停頓,直到指令執行完畢,服務才能恢復。這個時候可以使用 scan 指令,scan 指令可以無阻塞的提取出指定模式的 key 列表,但是會有一定的重複概率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用 keys 指令長。

注意:不過,增量式迭代命令也不是沒有缺點的: 舉個例子, 使用 SMEMBERS 命令可以返回集合鍵當前包含的所有元素, 但是對於 SCAN 這類增量式迭代命令來說, 因爲在對鍵進行增量式迭代的過程中, 鍵可能會被修改, 所以增量式迭代命令只能對被返回的元素提供有限的保證 。

11 Redis 是怎麼持久化的?

RDB 做鏡像全量持久化,AOF 做增量持久化。因爲 RDB 會耗費較長時間,不夠實時,在停機的時候會導致大量丟失數據,所以需要 AOF 來配合使用。在 Redis 實例重啓時,會使用 RDB 持久化文件重新構建內存,再使用 AOF 重放近期的操作指令來實現完整恢復重啓之前的狀態。

這裏很好理解,把 RDB 理解爲一整個表全量的數據,AOF 理解爲每次操作的日誌就好了,服務器重啓的時候先把表的數據全部搞進去,但是他可能不完整,你再回放一下日誌,數據不就完整了嘛。不過 Redis 本身的機制是 AOF 持久化開啓且存在 AOF 文件時,優先加載 AOF 文件;AOF 關閉或者 AOF 文件不存在時,加載 RDB 文件;加載 AOF/RDB 文件城後,Redis 啓動成功; AOF/RDB 文件存在錯誤時,Redis 啓動失敗並打印錯誤信息

12 如果突然機器掉電,Redis 是如何實現持久化的?

取決於 AOF 日誌 sync 屬性的配置,如果不要求性能,在每條寫指令時都 sync 一下磁盤,就不會丟失數據。但是在高性能的要求下每次都 sync 是不現實的,一般都使用定時 sync,比如1s 1次,這個時候最多就會丟失1s的數據。

13 淘汰策略

由於成本和內存限制,當存儲的數據超過緩存容量時,需要對緩存的數據進行剔除。一般的剔除策略有以下三種:

  1. FIFO 淘汰最早數據
  2. LRU 剔除最近最少使用
  3. LFU 剔除最近使用頻率最低的數據

14 什麼是 RDB 持久化?

RDB 持久化是默認的持久化方式,在指定的時間間隔內將內存中的數據集快照寫入磁盤。其實就是將內存中數據以快照的方式寫入到二進制文件中。Redis 在進行數據持久化的過程中,會先將數據寫入到一個臨時文件中,待持久化過程都結束了,纔會用這個臨時文件替換上次持久化好的文件。正是這種特性,讓我們可以隨時來進行備份,因爲快照文件總是完整可用的。

對於 RDB 方式,Redis 會單獨創建(fork)一個子進程來進行持久化,而主進程是不會進行任何 IO 操作的,這樣就確保了 Redis 極高的性能。

15 什麼是 AOF 持久化?

AOF,即 Append Only File,只允許追加不允許改寫的文件。其實 AOF 方式是將執行過的寫指令記錄下來,在數據恢復時按照從前到後的順序再將指令都執行一遍。

默認的 AOF 持久化策略是每秒鐘 fsync 一次(fsync 是指把緩存中的寫指令記錄到磁盤中),因爲在這種情況下,Redis 仍然可以保持很好的處理性能,即使 Redis 故障,也只會丟失最近1秒鐘的數據。

因爲採用了追加方式,如果不做任何處理的話,AOF 文件會變得越來越大,爲此,Redis 提供了 AOF 文件重寫(rewrite)機制,即當 AOF 文件的大小超過所設定的閾值時,Redis 就會啓動 AOF 文件的內容壓縮,只保留可以恢復數據的最小指令集。

在進行 AOF 重寫時,仍然是採用先寫臨時文件,全部完成後再替換的流程,所以斷電、磁盤滿等問題都不會影響 AOF 文件的可用性。

16 RDB 持久化與 AOF 持久化的優缺點對比?

Redis(2)–持久化,主從同步

17 RDB 和 AOF 兩者怎麼選擇?

單獨用 RDB 會丟失很多數據,單獨用 AOF 會導致數據恢復沒 RDB 快,當真出什麼問題時第一時間用 RDB 恢復,然後 AOF 做數據補全即可。

18 Redis 的同步機制?

Redis 可以使用主從同步,從從同步。第一次同步時,主節點做一次 bgsave,並同時將後續修改操作記錄到內存 buffer,待完成後將 RDB 文件全量同步到複製節點,複製節點接受完成後將 RDB 鏡像加載到內存。加載完成後,再通知主節點將期間修改的操作記錄同步到複製節點進行重放就完成了同步過程。後續的增量數據通過 AOF 日誌同步即可,有點類似數據庫的 binlog。

19 Redis 集羣?

  1. Redis Sentinal 着眼於高可用,在 master 宕機時會自動將 slave 提升爲 master,繼續提供服務。
  2. Redis Cluster 着眼於擴展性,在單個 Redis 內存不足時,使用 Cluster 進行分片存儲。

20 什麼是緩存雪崩?

對熱點數據我們一般會採用緩存,但是緩存一般是由定時任務去刷新的,可能會導致緩存的所有 Key 在同一時間同時失效,這會導致所有請求全部打到數據庫上去,將會導致災難性的結果。

21 如何解決緩存雪崩的問題?

在批量往 Redis 存數據的時候,把每個 Key 的失效時間都加個隨機值,保證數據不會在同一時間大面積失效。或者設置熱點數據永遠不過期,有更新操作就更新緩存就好了。

22 什麼是緩存穿透?

用戶不斷向緩存和數據庫中都沒有的數據發起請求,例如,數據庫 id 一般是從1自增上去的,如果用戶一直申請 id 爲 -1 的數據,每次都能繞開 Redis 直接打到數據庫,數據庫也查不到,很可能會導致數據庫壓力過大,嚴重的甚至會擊垮數據庫。

23 如何解決緩存穿透的問題?

  1. 在接口層增加校驗,比如用戶鑑權校驗,參數做校驗,不合法的參數直接代碼 Return,比如:id 做基礎校驗,id <=0 的直接攔截等。
  2. 從緩存取不到的數據,在數據庫中也沒有取到,這時也可以將對應 Key 的 Value 對寫爲 null、位置錯誤、稍後重試這樣的值,緩存有效時間可以設置短點,如30秒(設置太長會導致正常情況也沒法使用)。
  3. 爲了防止攻擊用戶反覆用同一個 id 暴力攻擊,可以在網關層 Nginx 中對每秒訪問次數超出閾值的 IP 進行拉黑處理。
  4. 使用布隆過濾器,利用高效的數據結構和算法快速判斷出 Key 是否在數據庫中存在,不存在直接 return 就好,存在就去查數據庫刷新緩存再 return。

24 什麼是緩存擊穿?

緩存擊穿與緩存雪崩類似,又有一些不同。緩存雪崩是因爲大面積的緩存失效打崩了數據庫,而緩存擊穿是指一個 Key 非常熱點,在不停的扛着大併發,大併發集中對這一個點進行訪問,當這個 Key 在失效的瞬間,持續的大併發就穿破緩存,直接請求數據庫,就像在一個完好無損的桶上鑿開了一個洞。

25 如何解決緩存擊穿的問題?

設置熱點數據永遠不過期。或者加上互斥鎖即可。

26 Redis 與關係型數據庫的區別?

在這裏插入圖片描述

27 服務器是多核的,Redis 卻是單線程的,會不會造成浪費?

可以在單機開多個 Redis 實例。

28 單機瓶頸問題如何解決?

使用集羣的部署方式 Redis cluster,並且是主從同步讀寫分離,類似 Mysql 的主從同步,Redis cluster 支撐 N 個 Redis master node,每個 master node 都可以掛載多個 slave node。

這樣整個 Redis 就可以橫向擴容了。如果你要支撐更大數據量的緩存,那就橫向擴容更多的 master 節點,每個 master 節點就能存放更多的數據了。

29 Redis 的哨兵模式?

Redis(3)–哨兵模式,集羣

30 Redis 爲什麼需要主從的架構模式?

單機 QPS (每秒內查詢次數)是有上限的,而且 Redis 的特性就是必須支撐讀高併發的,那一臺機器又讀又寫,很容易帶來性能的問題。但如果只讓這個 master 機器去寫,數據同步給別的 slave 機器,他們都拿去讀,分發掉大量的請求那是不是好很多,而且擴容的時候還可以輕鬆實現水平擴容。
在這裏插入圖片描述

31 主從同步是如何實現的?

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

32 Redis 的過期策略?

  1. 定期刪除:一段時間內隨機抽一些設置了過期時間的 key,檢查是否過期,過期就刪了
  2. 惰性刪除:不主動刪除,當查詢來了時檢測是否過期,過期就刪除不返回,沒過期就像原來一樣即可。

33 Redis 中的事務?

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

34 Redis 的高級用法 HyperLogLog?

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

35 String 的使用場景?

  1. 緩存功能:String 字符串是最常用的數據類型,不僅僅是 Redis,各個語言都是最基本類型,因此,利用 Redis 作爲緩存,配合其它數據庫作爲存儲層,利用 Redis 支持高併發的特點,可以大大加快系統的讀寫速度、以及降低後端數據庫的壓力。
  2. 計數器:許多系統都會使用 Redis 作爲系統的實時計數器,可以快速實現計數和查詢的功能。而且最終的數據結果可以按照特定的時間落地到數據庫或者其它存儲介質當中進行永久保存。
  3. 共享用戶 Session:用戶重新刷新一次界面,可能需要訪問一下數據進行重新登錄,或者訪問頁面緩存 Cookie,但是可以利用 Redis 將用戶的 Session 集中管理,在這種模式只需要保證 Redis 的高可用,每次用戶 Session 的更新和獲取都可以快速完成。大大提高效率。

36 Hash 的使用場景?

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

但是這個的場景其實還是多少單一了一些,因爲現在很多對象都是比較複雜的,比如商品對象可能裏面就包含了很多屬性,其中也有對象。

37 List 的使用場景?

  1. 消息隊列:Redis 的鏈表結構,可以輕鬆實現阻塞隊列,可以使用左進右出的命令組成來完成隊列的設計。比如:數據的生產者可以通過 Lpush 命令從左邊插入數據,多個數據消費者,可以使用 BRpop 命令阻塞的“搶”列表尾部的數據。
  2. 文章列表或者數據分頁展示的應用。比如,我們常用的博客網站的文章列表,當用戶量越來越多時,而且每一個用戶都有自己的文章列表,而且當文章多時,都需要分頁展示,這時可以考慮使用 Redis 的列表,列表不但有序同時還支持按照範圍內獲取元素,可以完美解決分頁查詢功能。大大提高查詢效率。

38 Set 的使用場景?

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

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

39 Sorted Set 的使用場景?

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

  1. 排行榜:有序集合經典使用場景。例如視頻網站需要對用戶上傳的視頻做排行榜,榜單維護可能是多方面:按照時間、按照播放量、按照獲得的贊數等。
  2. 用 Sorted Sets 來做帶權重的隊列,比如普通消息的 score 爲1,重要消息的 score 爲2,然後工作線程可以選擇按 score 的倒序來獲取工作任務。讓重要的任務優先執行。

40 如果多個系統同時操作(併發)Redis 帶來的數據問題?如何解決?

在這裏插入圖片描述
A、B、C 三個系統,分別去操作 Redis 的同一個 Key,本來順序是1,2,3是正常的,但是因爲系統 A 網絡突然抖動了一下,B,C 在他前面操作了 Redis,導致出錯。

解決方法:我們可以使用 Zookeeper 幫助我們管理數據。

在這裏插入圖片描述
某個時刻,多個系統實例都去更新某個 key。可以基於 Zookeeper 實現分佈式鎖。每個系統通過 Zookeeper 獲取分佈式鎖,確保同一時間,只能有一個系統實例在操作某個 Key,別人都不允許讀和寫。

要寫入緩存的數據,都是從 MySQL 裏查出來的,都得寫入 MySQL 中,寫入 MySQL 中的時候必須保存一個時間戳,從 MySQL 查出來的時候,時間戳也查出來。

每次要寫之前,先判斷一下當前這個 Value 的時間戳是否比緩存裏的 Value 的時間戳要新。如果是的話,那麼可以寫,否則,就不能用舊的數據覆蓋新的數據。

41 緩存與數據庫的雙寫一致性問題?

數據庫和緩存的雙寫一致性問題.

42 Cache Aside Pattern 爲什麼是刪除緩存,而不是更新緩存?

Cache Aside Pattern 是最經典的緩存+數據庫讀寫的模式,讀的時候,先讀緩存,緩存沒有的話,就讀數據庫,然後取出數據後放入緩存,同時返回響應。更新的時候,先更新數據庫,然後再刪除緩存。

至於爲什麼是刪除緩存,而不是更新緩存?原因是更新緩存的代價有時候是很高的。如果你頻繁修改一個緩存涉及的多個表,緩存也頻繁更新,但這個緩存不會被頻繁訪問到,豈不是會有大量的冷數據?在這種情況下,用到緩存纔去算緩存,會使開銷大幅度降低。說到底,這是一種 Lazy 計算的思想,不要每次都重新做複雜的計算,不管它會不會用到,而是讓它到需要被使用的時候再重新計算。

43 Redis 的線程模型?

Redis 內部使用文件事件處理器 file event handler,這個文件事件處理器是單線程的,所以 Redis 才叫做單線程的模型。它採用 IO 多路複用機制同時監聽多個 Socket,根據 Socket 上的事件來選擇對應的事件處理器進行處理。

文件事件處理器的結構包含 4 個部分:

  1. 多個 Socket
  2. IO 多路複用程序
  3. 文件事件分派器
  4. 事件處理器(連接應答處理器、命令請求處理器、命令回覆處理器)

多個 Socket 可能會併發產生不同的操作,每個操作對應不同的文件事件,但是 IO 多路複用程序會監聽多個 Socket,會將 Socket 產生的事件放入隊列中排隊,事件分派器每次從隊列中取出一個事件,把該事件交給對應的事件處理器進行處理。

參考:《吊打面試官》系列-Redis基礎知識
《吊打面試官》系列-緩存雪崩、擊穿、穿透
《吊打面試官》系列-Redis哨兵、持久化、主從、手撕LRU
《吊打面試官》系列-Redis雙寫一致性、併發競爭、線程模型
《吊打面試官》系列-Redis常見面試題(帶答案)
redis和Memcached的區別,都什麼時候使用?

發佈了123 篇原創文章 · 獲贊 226 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章