Redis 緩存框架面試題全解析

概述

隨著應用研發技術的不斷成熟,Redis 緩存技術已經成為后臺研發同學必備的能力之一。在很多公司面試的過程中,都必不可少地考察 Redis 知識點的掌握。

在這里我將所掌握的 Redis 的面試題目進行詳細的梳理,結合平時工作遇到的問題以及面試題庫收集的內容給大家進行分享。

本文將 Redis 知識方向分為六大塊進行梳理:

  1. Redis 概念理解
  2. Redis 數據結構與指令
  3. Redis 高并發處理策略
  4. Redis 集群結構以及設計理念
  5. Redis 緩存管理與持久化機制
  6. Redis 應用場景設計

通過本文的學習,相信對大家未來的面試以及知識點的掌握都會有所助益。

一、Redis 概念理解

1. 什么是 Redis?

Redis 全稱為:Remote Dictionary Server(遠程數據服務),是一個基于內存且支持持久化的高性能 key-value 數據庫。

具備一下幾個基本特征:

  1. 多數據類型
  2. 持久化機制
  3. 主從同步

2. Redis 的特點有哪些?

  1. Redis 本質上是一個 key-value 類型的數據庫
  2. 整個數據庫都是在內存中進行操作,可定期刷新到磁盤進行持久化存儲
  3. 由于是在內存操作,讀寫能力非常好,每秒可以處理 10 萬次讀寫操作
  4. Redis 支持多種數據結構,提供了豐富的數據類型選擇
  5. Redis 同時支持數據備份,主從配置
  6. Redis 的所有操作都是原子性的

3. Memcache 與 Redis 的區別都有哪些?

  1. 存儲方式不同:Memcache 把數據全部存在內存之中,斷電后會丟失。Redis 所有數據加載在內存,但也會持久化到磁盤,保證數據的持久性。
  2. 支持數據類型不同:Memcache 對數據類型支持相對簡單,只支持 key-value 結構。Redis 有復雜的數據類型。
  3. 底層模型不同:底層實現方式以及客戶端通信應用協議不一樣。 Redis 直接自己構建了 VM 機制。
  4. 運行環境不同:Redis 目前官方只支持 Linux 上運行。

4. Redis 相比 Memcached 有哪些優勢?

  1. Memcached 所有的值均是簡單的字符串,Redis 作為其替代者,支持更為豐富的數據類型
  2. Redis 的速度比 Memcached 快很多
  3. Redis 可以持久化其數據

5. 如何實現本地緩存?請描述一下你知道的方式

  1. 程序中定義內存數據結構來實現, 比如說定義一個成員變量Map 或者 List 均可以實現
  2. 使用開源的緩存框架 Ehcache,Ehcache 封裝了對于內存操作的功能
  3. Guava Cache 是 Google 開源的工具集, 提供了緩存的邊界操作工具

6. Redis 通訊協議是什么?有什么特點?

Redis 的通信協議是 Redis Serialization Protocol,簡稱 RESP。

有如下特性:

  1. 是二進制安全的
  2. 在 TCP 層
  3. 基于請求—響應的模式

二、Redis 數據結構與指令

1. Redis 支持的數據類型

  1. String(字符串)
  2. list(列表):list 是字符串列表,按照插入順序排序。元素可以在列表的頭部(左邊)或者尾部(右邊)進行添加。
  3. hash(哈希):Redis hash 是一個鍵值對(key-value)集合。Redis hash 是一個 String 類型的 field 和 value 的映射表,hash 特別適合用于存儲對象。
  4. set(集合):Redis 的 set 是 String 類型的無序集合。
  5. zset(sorted set:有序集合):Redis zset 和 set 一樣也是 String 類型元素的集合,且不允許重復的成員。不同的 zset 是每個元素都會關聯一個 double 類型的分數。zset 通過這個分數來為集合中所有元素進行從小到大的排序。zset 的成員是唯一的,但分數(score)卻可以重復。

2. Redis 常用的命令有哪些?

在這里插入圖片描述

3. 一個字符串類型的值能存儲最大容量是多少?

512M

4. Redis 各個數據類型最大存儲量分別是多少?

  1. Strings 類型:一個 String 類型的 value 最大可以存儲 512M
  2. Lists 類型:list 的元素個數最多為 2^32-1 個,也就是 4294967295 個。
  3. Sets 類型:元素個數最多為 2^32-1 個,也就是 4294967295 個。
  4. Hashes 類型:鍵值對個數最多為 2^32-1 個,也就是 4294967295 個。
  5. Sorted sets 類型:跟 Sets 類型相似。

5. 請介紹一下 Redis 的數據類型 SortedSet(zset)以及底層實現機制?

zset 有順序,不能重復。在業務場景下,適合做排行榜之類的事情。

底層實現機制:

SortedSet 的實現方式可能有兩種:ziplist 或者 skiplist

當有序集合對象同時滿足以下兩個條件時,對象使用 ziplist 編碼:

  1. 保存的元素數量小于 128;

  2. 保存的所有元素長度都小于 64 字節。    不能滿足上面兩個條件的使用 skiplist 編碼。

  3. ziplist 編碼的有序集合對象使用壓縮列表作為底層實現,每個集合元素使用兩個緊挨在一起的壓縮列表節點來保存,第一個節點保存元素的成員,第二個節點保存元素的分值。并且壓縮列表內的集合元素按分值從小到大的順序進行排列,小的放置在靠近表頭的位置,大的放置在靠近表尾的位置。

  4. skiplist 編碼的有序集合對象使用 zset 結構作為底層實現,一個 zset 結構同時包含一個字典和一個跳躍表。字典的鍵保存元素的值,字典的值則保存元素的分值;跳躍表節點的 object 屬性保存元素的成員,跳躍表節點的 score 屬性保存元素的分值。

6. Redis 事務相關命令有哪些?

  1. discard 命令:取消事務,丟棄事務中所有命令。
  2. exec 命令:執行所有事務內的命令。
  3. multi 命令:標記一個事務開始。
  4. unwatch 命令:取消 watch 命令對所有 key 的監視。
  5. watch 命令:監視一個(或多個)key,如果在執行事務之前這個(這些)key 被其他命令所改動,事務將被打斷。

7. 什么是 Redis 事務?原理是什么?

Redis 中的事務是一組命令的集合,是 Redis 的最小執行單位,一個事務要么都執行,要么都不執行。

Reids 事務保證一個事務內的命令依次執行,而不會被其他命令插入。

Redis 事務的原理是先將屬于一個事務的命令發送給 Redis,然后依次執行這些命令。

8. Redis 事務的注意點有哪些?

  1. 不支持回滾,如果事務中有錯誤的操作,無法回滾到處理前的狀態,需要開發者處理。
  2. 在執行完當前事務內所有指令前,不會同時執行其他客戶端的請求。

9. Redis 為什么不支持回滾?

Redis 事務不支持回滾,如果遇到問題,會繼續執行余下的命令。 這一點和關系型數據庫不太一致。這樣處理的原因有:

  1. 只有語法錯誤,Redis才會執行失敗,例如錯誤類型的賦值, 這就是說從程序層面完全可以捕獲以及解決這些問題
  2. 支持回滾需要增加很多工作,不支持的情況下,Redis 可以保持簡單、速度快的特性

10. 請介紹一下 Redis 的 Pipeline(管道),以及使用場景

Redis 客戶端與服務端通信模型使用的 TCP 協議進行連接, 那么在單個指令的執行過程中,都會存在“交互往返”的時間。

Redis 提供了批量操作命令,例如 mget、mset 等,能夠一定程度上節省這類時間,但大部分命令還是不支持批量操作。

因此 Pipeline 功能,能夠改善這一類問題。 Pipeline 將一組 Redis 命令進行組裝,一次性傳輸給 Redis,再將這些命令執行結果,按照順序返回給客戶端。

適用場景:有批量的數據寫入 Redis,并且這些數據允許一定比例的寫入失敗,那么這種場景就可以適用 Pipeline。失敗的數據后期進行補償即可。

11. 請說明一下 Redis 的批量命令與 Pipeline 有什么不同?

  1. 批量命令保證原子性的,Pipeline 非原子性
  2. 批量命令是一個命令對應多個 key,Pipeline 支持多個命令
  3. 批量命令是 Redis 服務端實現,而 Pipeline 是需要服務端和客戶端共同實現

12. 請介紹一下 Redis 的發布訂閱功能

Redis 提供了基于“發布/訂閱”模式的消息機制,消息發布者和訂閱者不能直接通信,客戶端發布消息的時候指定發送的頻道,然后訂閱了該頻道的用戶可以接收到該消息。具體指令如下:

  1. 發布消息:publish channel message
  2. 訂閱消息:subscribe channel [……]
  3. 退訂消息:punsubscribe

13. Redis 的鏈表數據結構的特征有哪些?

  1. 雙端引用:鏈表的最前和最后節點都有引用,獲取前后節點的復雜度為 o(1)
  2. 無環鏈表:對于鏈表的訪問都是以 null 結束
  3. 長度計數器:通過 len 屬性來記錄鏈表長度

14. 請介紹一下 Redis 的 String 類型底層實現?

Redis 底層實現了簡單動態字符串的類型(SSD),來表示 String 類型。 沒有直接使用 C 語言定義的字符串類型。

struct sdshdr{
    //記錄 buf 數組中已使用字節的數量
    //等于 SDS 保存字符串的長度
    int len;
    //記錄 buf 數組中未使用字節的數量
    int free;
    //字節數組,用于保存字符串
    char buf[];
}

15. Redis 的 String 類型使用 SSD 方式實現的好處?

  1. 避免緩沖區溢出:進行字符修改時候,可以根據 len 屬性來檢查空間是否滿足要求
  2. 減少內存分配次數:len 和 free 兩個屬性,可以協助進行空間預分配以及惰性空間釋放
  3. 二進制安全:SSD 不是以空字符串來判斷是否結束,而是以 len 屬性來判斷字符串是否結束
  4. 常數級別獲取字符串長度:獲取字符串的長度只需要讀取 len 屬性就可以獲取
  5. 兼容 C 字符串函數:可以重用 C 語言庫的 <string.h style="box-sizing: border-box; -webkit-tap-highlight-color: transparent; text-size-adjust: none; -webkit-font-smoothing: antialiased; outline: 0px !important;">的一部分函數</string.h>

16. 設置鍵的生存時間和過期時間有哪些命令?

  • EXPIRE 以秒為單位,設置鍵的生存時間
  • PEXPIRE 以毫秒為單位,設置鍵的生存時間
  • EXPIREAT 以秒為單位,設置鍵的過期 UNIX 時間戳
  • PEXPIREAT 以毫秒為單位,設置鍵的過期 UNIX 時間戳

三、Redis 高并發處理策略

1. 為什么 Redis 需要把所有數據放到內存中?

追求更快的讀寫速度,并異步方式持久化存儲到磁盤。 如果不將數據放到內存中,磁盤的 I/O 速度會嚴重影響 Redis 的性能。

2. Redis 是單線程的嗎?

是。

這里的單線程指的是 Redis 網絡請求模塊使用了一個線程(所以不需考慮并發安全性),即一個線程處理所有網絡請求,其他模塊仍用了多個線程。

3. Redis 為什么設計成單線程的?

  1. 絕大部分請求是純粹的內存操作(非常快速)
  2. 采用單線程,避免了不必要的上下文切換和競爭條件
  3. 非阻塞 IO,內部采用 epoll,epoll 中的讀、寫、關閉、連接都轉化成了事件,然后利用 epoll 的多路復用特性,避免 IO 代價。

4. 什么是緩存穿透?怎么解決?

緩存穿透是指緩存中查詢一個不存在的數據,需要去數據庫中獲取。

如果數據庫也查不到結果,將不會同步到緩存,導致這個不存在數據每次請求都要到數據庫查詢,失去了緩存的意義。

解決方法有兩個:

1. 布隆過濾(Bloom filter)

將所有查詢的參數都存儲到一個 bitmap 中,在查詢緩存之前,先再找個 bitmap 里面進行驗證。

如果 bitmap 中存在,則進行底層緩存的數據查詢; 如果 bitmap 中不存在查詢參數,則進行攔截,不再進行緩存的數據查詢。

適用范圍:可以用來實現數據字典,進行數據的判重,或者集合求交集

2. 緩存空對象

如果查詢返回的數據為空,仍然把這個空結果進行緩存。那么再次用相同 key 獲取數據的時候,即使不存在的數據,緩存也可以直接返回空值,避免重復訪問 DB。

緩存空對象有兩個不足之處:

  1. 緩存層將存儲更多的鍵值對,如果是惡意的隨機訪問,將造成很多內存空間的浪費。這個不足之處可以通過將這類數據設置很短的過期時間來控制。
  2. DB 與緩存數據不一致。這種可以考慮通過異步消息來進行數據更新的通知,在一定程度上減少這類不一致的時間。

5. 什么是緩存雪崩? 怎么解決?

在集中的一段時間內,有大量的緩存失效,導致大量的訪問沒有命中緩存,從而將所有查詢進行數據庫訪問,導致數據庫的壓力增大,從而造成了緩存雪崩。

比如,如果要做一個促銷活動,我們將商品信息都刷新到緩存, 過期時間統一為 30 分鐘。那么在 30 分鐘之后,這批商品將全部過期。這時候這批商品的訪問查詢,都落到了數據庫,對于數據庫而言,這一刻的壓力會非常大。從而造成系統整體性風險。

解決方案:

方法 1:分散失效時間

分析緩存數據的特點,盡量將熱點緩存的失效時間均勻分布。 比如說將相同類型的緩存的失效時間設置成一個在一定區間內的隨機值。從而有效的分散失效時間。

方法 2:DB 訪問限制

對數據的訪問進行限流性質的操作。比如說對數據庫訪問進行加鎖的處理或者限流相關的處理。

方法 3:多級緩存設計

一級緩存為基礎緩存,緩存失效時間設置一個較長時間, 二級緩存為應用緩存,失效時間正常設置,一般會比較短。 當二級緩存失效的時候,再從一級緩存里面獲取。

6. 緩存的更新策略有幾種?分別有什么注意事項?

緩存的更新策略包含:

  1. 先更新數據庫,再更新緩存
  2. 先刪除緩存,再更新數據庫
  3. 先更新數據庫,再刪除緩存

策略一:先更新數據庫,再更新緩存

1. 這種策略會導致線程安全問題

例如:線程 1 更新了數據庫,線程 2 也更新數據庫, 這時候由于某種原因,線程 2 首先更新了緩存,線程 1 后續更新。 這樣就導致了臟數據的問題。 因為目前數據庫中存儲的線程 2 更新后的數據,而緩存存儲的是線程1更新的老數據。

2. 更新緩存的復雜度相對較高

數據寫入數據庫之后,一般存入緩存的數據都要經過一系列的加工計算,然后寫入緩存。 這時候更新緩存相比較于直接刪除緩存要比較復雜。

策略二:先刪除緩存,再更新數據庫

這種策略可能導致數據不一致的問題。線程 1 寫數據刪除緩存;這時候有線程 2 查詢該緩存,發現不存在,則去訪問數據庫,得到舊值放入緩存;線程 1 更新數據庫。這時候就出現了數據不一致的問題。 如果緩存沒有過期時間,這個臟數據一直存在。

解決方案:在寫數據庫成功之后, 再次淘汰緩存一次。

策略三:先更新數據庫,再刪除緩存

可能會造成比較短暫的數據不一致。在更新完成數據庫, 還沒有刪除緩存的時刻,如果有緩存數據訪問, 就會造成數據不一致的情形。 但這種如果數據同步機制比較科學,一般都會比較快, 不一致的影響比較小。

7. 請介紹幾個可能導致 Redis 阻塞的原因

內部原因:

  1. Redis 的 API 或者指令數據結構使用不合理
  2. Redis 主機 CPU 負載過高,導致系統崩潰
  3. 持久化工作資源占用過多

外部原因:

  1. CPU 競爭
  2. 內存交換
  3. 網絡問題

8. 怎么去發現 Redis 阻塞異常情況?

1. 通過應用服務監控發現

當 Redis 阻塞的時候,線上應用服務應該會感知發現。比如說發現 Redis 鏈接超時等。這種就需要應用服務增加對于異常的統計,并針對 Redis 相關的異常,進行報警。

2. 通過 Redis 自身監控系統

借助 Redis 監控系統發現阻塞問題,當監控系統發現各個監控指標存在異常的時候,發送報警。 指標有:CPU/內存/磁盤等, 慢查詢,命令耗時增加等。

四、Redis 集群結構以及設計理念

1. Redis 集群架構模式有哪幾種?

  1. Redis 單節點單機器部署
  2. Redis 主從節點部署
  3. Redis Sentinel(哨兵)模式部署
  4. Redis 集群模式

2. Redis 集群最大節點個數是多少?

16384 個

3. Redis 集群的主從復制模型是怎樣的?

Redis 中的主從復制,也就是 Master-Slave 模型,多個 Redis 實例間的數據同步以及 Redis 集群中數據同步會使用主從復制。

主從復制主要是數據同步, 數據同步分為全量同步增量同步

全量同步:

全量同步一般發生在 Slave 機器初始化階段, 這時候 Slave 需要將 Master 上的所有數據都進行同步復制。

大概步驟如下所示:

https://images.gitbook.cn/ea87acc0-fbc5-11e9-a1fa-3962553c2766
  1. 從服務器發送 SYNC 命令,鏈接主服務器
  2. 主服務器收到 SYNC 命令后,進行存盤的操作,并繼續收集后續的寫命令,存儲緩沖區
  3. 存盤結束后,將對應的數據文件發送到 Slave 中,完成一次全量同步
  4. 主服務數據發送完畢后,將進行增量的緩沖區數據同步
  5. Slave 加載數據文件和緩沖區數據,開始接受命令請求,提供操作

增量更新:

Slave 節點初始化完成之后,開始正常工作,Master 節點進行的寫操作都會同步到 Slave 節點上面。Master 節點每執行一個寫命令都會向從服務器發送相同的寫命令,從服務器接收到命令,并執行。

4. 請介紹一下 Redis 集群實現方案

1. Redis Cluster 集群方案(服務端 Sharding 技術)

Redis Cluster 是 3.0 版本開始正式提供的服務器 Sharding 技術。引入 slot(槽)的概念,整個集群共有 16384 個槽。根據 key 進行散列(CRC16 后 16384 取模),分配到其中一個槽當中。

2. Redis Sharding 集群(客戶端 Sharding 技術)

Redis Sharding 出現先與 Redis Cluster 方案,他是多 Redis 實例集群方案。

客戶端 Sharding 方式,該向哪個 Redis 節點操作數據,由客戶端來進行控制。服務端 Redis 還是一個個相對獨立的 Redis 實例節點,沒有做任何變動。節點選擇可采用的方法:一致性 hash 算法或者虛擬節點算法。

還有 31% 的精彩內容
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
支付 ¥29.99 繼續閱讀

推薦閱讀更多精彩內容