天天看點

硬核!Redis 面試知識點總結,建議收藏!

Redis 面試知識點總結

      • 1、Redis是什麼
      • 2、Redis支援的資料類型
      • 3、在實際項目中使用緩存有遇到什麼問題或者會遇到什麼問題你知道嗎?
      • 4、什麼是Redis雪崩
      • 5、如何解決Redis雪崩
      • 6、了解緩存穿透和擊穿麼,可以說說他們跟雪崩的差別嗎?
      • 7、怎麼解決緩存穿透
      • 8、怎麼解決緩存擊穿
      • 9、Redis為何能這麼快
      • 10、Redis 和 Memcached 的差別
      • 11、Redis支援淘汰政策
      • 12、談一下對Redis 的持久化機制了解
      • 13、RDB是怎麼工作的
      • 14、AOF工作原理
      • 15、RDB與AOF應該如何選擇?
      • 16、你能說說 Redis 主從複制的過程和原理嗎?
      • 17、Reids主從資料同步的過程
      • 18、能具體說下全量複制和部分複制的過程嗎?
      • 19、主從複制會存在哪些問題呢?
      • 20、怎麼解決主從複制存在的問題,哨兵的功能
      • 21、哨兵的工作原理
    • 總結

以下是我從網上整理的Redis核心面試題總結,希望可以幫助大家,不喜勿碰!!!

1、Redis是什麼

Redis 是 C 語言開發的一個開源的(遵從 BSD 協定)高性能鍵值對(key-value)的記憶體資料庫,可以用作資料庫、緩存、消息中間件等。

它是一種 NoSQL(not-only sql,泛指非關系型資料庫)的資料庫。

  • 性能優秀,資料在記憶體中,讀寫速度非常快,支援并發 10W QPS。
  • 單程序單線程,是線程安全的,采用 IO 多路複用機制。
  • 豐富的資料類型,支援字元串(strings)、散列(hashes)、清單(lists)、集合(sets)、有序集合(sorted sets)等。
  • 支援資料持久化。

    可以将記憶體中資料儲存在磁盤中,重新開機時加載。

  • 主從複制,哨兵,高可用。
  • 可以用作分布式鎖。
  • 可以作為消息中間件使用,支援釋出訂閱。

2、Redis支援的資料類型

硬核!Redis 面試知識點總結,建議收藏!

首先 Redis 内部使用一個 redisObject 對象來表示所有的 key 和 value。

redisObject 最主要的資訊如上圖所示:type 表示一個 value 對象具體是何種資料類型,encoding 是不同資料類型在 Redis 内部的存儲方式。

比如:type=string 表示 value 存儲的是一個普通字元串,那麼 encoding 可以是 raw 或者 int。

①String 是 Redis 最基本的類型,可以了解成與 Memcached一模一樣的類型,一個 Key 對應一個 Value。Value 不僅是 String,也可以是數字。

String 類型是二進制安全的,意思是 Redis 的 String 類型可以包含任何資料,比如 jpg 圖檔或者序列化的對象。String 類型的值最大能存儲 512M。

②Hash是一個鍵值(key-value)的集合。Redis 的 Hash 是一個 String 的 Key 和 Value 的映射表,Hash 特别适合存儲對象。常用指令:hget,hset,hgetall 等。

③List 清單是簡單的字元串清單,按照插入順序排序。可以添加一個元素到清單的頭部(左邊)或者尾部(右邊) 常用指令:lpush、rpush、lpop、rpop、lrange(擷取清單片段)等。

應用場景:List 應用場景非常多,也是 Redis 最重要的資料結構之一,比如 Twitter 的關注清單,粉絲清單都可以用 List 結構來實作。

資料結構:List 就是連結清單,可以用來當消息隊列用。Redis 提供了 List 的 Push 和 Pop 操作,還提供了操作某一段的 API,可以直接查詢或者删除某一段的元素。

實作方式:Redis List 的是實作是一個雙向連結清單,既可以支援反向查找和周遊,更友善操作,不過帶來了額外的記憶體開銷。

④Set 是 String 類型的無序集合。集合是通過 hashtable 實作的。Set 中的元素是沒有順序的,而且是沒有重複的。常用指令:sdd、spop、smembers、sunion 等。

應用場景:Redis Set 對外提供的功能和 List 一樣是一個清單,特殊之處在于 Set 是自動去重的,而且 Set 提供了判斷某個成員是否在一個 Set 集合中。

⑤Zset 和 Set 一樣是 String 類型元素的集合,且不允許重複的元素。常用指令:zadd、zrange、zrem、zcard 等。

使用場景:Sorted Set 可以通過使用者額外提供一個優先級(score)的參數來為成員排序,并且是插入有序的,即自動排序。

當你需要一個有序的并且不重複的集合清單,那麼可以選擇 Sorted Set 結構。

和 Set 相比,Sorted Set關聯了一個 Double 類型權重的參數 Score,使得集合中的元素能夠按照 Score 進行有序排列,Redis 正是通過分數來為集合中的成員進行從小到大的排序。

實作方式:Redis Sorted Set 的内部使用 HashMap 和跳躍表(skipList)來保證資料的存儲和有序,HashMap 裡放的是成員到 Score 的映射。

而跳躍表裡存放的是所有的成員,排序依據是 HashMap 裡存的 Score,使用跳躍表的結構可以獲得比較高的查找效率,并且在實作上比較簡單。

資料類型應用場景總結:

硬核!Redis 面試知識點總結,建議收藏!

3、在實際項目中使用緩存有遇到什麼問題或者會遇到什麼問題你知道嗎?

緩存和資料庫資料一緻性問題:分布式環境下非常容易出現緩存和資料庫間資料一緻性問題,針對這一點,如果項目對緩存的要求是強一緻性的,那麼就不要使用緩存。

我們隻能采取合适的政策來降低緩存和資料庫間資料不一緻的機率,而無法保證兩者間的強一緻性。

合适的政策包括合适的緩存更新政策,更新資料庫後及時更新緩存、緩存失敗時增加重試機制。

4、什麼是Redis雪崩

我了解的,目前電商首頁以及熱點資料都會去做緩存,一般緩存都是定時任務去重新整理,或者查不到之後去更新緩存的,定時任務重新整理就有一個問題。

舉個栗子:如果首頁所有 Key 的失效時間都是 12 小時,中午 12 點重新整理的,我零點有個大促活動大量使用者湧入,假設每秒 6000 個請求,本來緩存可以抗住每秒 5000 個請求,但是緩存中所有 Key 都失效了。

此時 6000 個/秒的請求全部落在了資料庫上,資料庫必然扛不住,真實情況可能 DBA 都沒反應過來直接挂了。

此時,如果沒什麼特别的方案來處理,DBA 很着急,重新開機資料庫,但是資料庫立馬又被新流量給打死了。這就是我了解的緩存雪崩。

5、如何解決Redis雪崩

處理緩存雪崩簡單,在批量往 Redis 存資料的時候,把每個 Key 的失效時間都加個随機值就好了,這樣可以保證資料不會再同一時間大面積失效。

如果 Redis 是叢集部署,将熱點資料均勻分布在不同的 Redis 庫中也能避免全部失效。

或者設定熱點資料永不過期,有更新操作就更新緩存就好了(比如運維更新了首頁商品,那你刷下緩存就好了,不要設定過期時間),電商首頁的資料也可以用這個操作,保險。

6、了解緩存穿透和擊穿麼,可以說說他們跟雪崩的差別嗎?

緩存穿透是指緩存和資料庫中都沒有的資料,而使用者(黑客)不斷發起請求。

舉個栗子:我們資料庫的 id 都是從 1 自增的,如果發起 id=-1 的資料或者 id 特别大不存在的資料,這樣的不斷攻擊導緻資料庫壓力很大,嚴重會擊垮資料庫。

至于緩存擊穿嘛,這個跟緩存雪崩有點像,但是又有一點不一樣,緩存雪崩是因為大面積的緩存失效,打崩了 DB。

而緩存擊穿不同的是緩存擊穿是指一個 Key 非常熱點,在不停地扛着大量的請求,大并發集中對這一個點進行通路,當這個 Key 在失效的瞬間,持續的大并發直接落到了資料庫上,就在這個 Key 的點上擊穿了緩存。

7、怎麼解決緩存穿透

  • 緩存穿透我會在接口層增加校驗,比如使用者鑒權,參數做校驗,不合法的校驗直接 return,比如 id 做基礎校驗,id<=0 直接攔截。
  • Redis 裡還有一個進階用法布隆過濾器(Bloom Filter)這個也能很好的預防緩存穿透的發生。它的原理也很簡單,就是利用高效的資料結構和算法快速判斷出你這個 Key 是否在資料庫中存在,不存在你 return 就好了,存在你就去查 DB 重新整理 KV 再 return。

8、怎麼解決緩存擊穿

緩存擊穿的話,設定熱點資料永不過期,或者加上互斥鎖(代碼如下)

public static String getData(String key) throws InterruptedException {
    //從Redis查詢資料
    String result = getDataByKV(key);
    //參數校驗
    if (StringUtils.isBlank(result)) {
        try {
            //獲得鎖
            if (reenLock.tryLock()) {
                //去資料庫查詢
                result = getDataByDB(key);
                //校驗
                if (StringUtils.isNotBlank(result)) {
                    //插進緩存
                    setDataToKV(key, result);
                }
            } else {
                //睡一會再拿
                Thread.sleep(100L);
                result = getData(key);
            }
        } finally {
            //釋放鎖
            reenLock.unlock();
        }
    }
    return result;
}
           

9、Redis為何能這麼快

官方提供的資料可以達到 100000+ 的 QPS(每秒内的查詢次數),Redis 确實是單程序單線程的模型,因為 Redis 完全是基于記憶體的操作,CPU 不是 Redis 的瓶頸,Redis 的瓶頸最有可能是機器記憶體的大小或者網絡帶寬。

既然單線程容易實作,而且 CPU 不會成為瓶頸,那就順理成章的采用單線程的方案了(畢竟采用多線程會有很多麻煩)。

啪啪啪打臉

翻車現場:Redis 6.0在19年年底這個美好的日子裡悄無聲息的釋出了,這次釋出在IT圈猶如一顆驚雷一般,因為這是redis最大的一次改版,首次加入了多線程。
  • Redis 完全基于記憶體,絕大部分請求是純粹的記憶體操作,非常迅速,資料存在記憶體中,類似于 HashMap,HashMap 的優勢就是查找和操作的時間複雜度是 O(1)。
  • 資料結構簡單,對資料操作也簡單。
  • 采用單線程,避免了不必要的上下文切換和競争條件,不存在多線程導緻的 CPU 切換,不用去考慮各種鎖的問題,不存在加鎖釋放鎖操作,沒有死鎖問題導緻的性能消耗。
  • 使用多路複用 IO 模型,非阻塞 IO。

10、Redis 和 Memcached 的差別

我:原因有如下四點:

  • 存儲方式上:Memcache 會把資料全部存在記憶體之中,斷電後會挂掉,資料不能超過記憶體大小。Redis 有部分資料存在硬碟上,這樣能保證資料的持久性。
  • 資料支援類型上:Memcache 對資料類型的支援簡單,隻支援簡單的 key-value,,而 Redis 支援五種資料類型。
  • 使用底層模型不同:它們之間底層實作方式以及與用戶端之間通信的應用協定不一樣。Redis 直接自己建構了 VM 機制,因為一般的系統調用系統函數的話,會浪費一定的時間去移動和請求。
  • Value 的大小:Redis 可以達到 1GB,而 Memcache 隻有 1MB。

11、Redis支援淘汰政策

政策 描述
volatile-lru 從已設定過期時間的KV集中優先對最近最少使用的資料淘汰
volatile-ttl 從已設定過期時間的KV集中優先對剩餘時間短的資料淘汰
volatile-random 從已設定過期時間的KV集中随機選擇資料淘汰
allkeys-lru 從所有KV集中優先對最近最少使用的資料淘汰
allkeys-random 從所有KV集中随機選擇資料淘汰
noeviction 不淘汰政策,若超過最大記憶體,傳回錯誤資訊

Redis 4.0 加入了 LFU(least frequency use)淘汰政策,包括 volatile-lfu 和 allkeys-lfu,通過統計通路頻率,将通路頻率最少,即最不經常使用的 KV 淘汰。

12、談一下對Redis 的持久化機制了解

Redis 為了保證效率,資料緩存在了記憶體中,但是會周期性的把更新的資料寫入磁盤或者把修改操作寫入追加的記錄檔案中,以保證資料的持久化。

Redis 的持久化政策有兩種:

  • RDB:快照形式是直接把記憶體中的資料儲存到一個 dump 的檔案中,定時儲存,儲存政策。
  • AOF:把所有的對 Redis 的伺服器進行修改的指令都存到一個檔案裡,指令的集合。Redis 預設是快照 RDB 的持久化方式。

當 Redis 重新開機的時候,它會優先使用 AOF 檔案來還原資料集,因為 AOF 檔案儲存的資料集通常比 RDB 檔案所儲存的資料集更完整。你甚至可以關閉持久化功能,讓資料隻在伺服器運作時存。

13、RDB是怎麼工作的

預設 Redis 是會以快照"RDB"的形式将資料持久化到磁盤的一個二進制檔案 dump.rdb。

工作原理:當 Redis 需要做持久化時,Redis 會 fork 一個子程序,子程序将資料寫到磁盤上一個臨時 RDB 檔案中。

當子程序完成寫臨時檔案後,将原來的 RDB 替換掉,這樣的好處是可以 copy-on-write。

RDB 的優點是:這種檔案非常适合用于備份:比如,你可以在最近的 24 小時内,每小時備份一次,并且在每個月的每一天也備份一個 RDB 檔案。

這樣的話,即使遇上問題,也可以随時将資料集還原到不同的版本。RDB 非常适合災難恢複。

RDB 的缺點是:如果你需要盡量避免在伺服器故障時丢失資料,那麼RDB不合适你。

14、AOF工作原理

使用 AOF 做持久化,每一個寫指令都通過 write 函數追加到 appendonly.aof 中,配置方式如下:

appendfsync yes   
appendfsync always     #每次有資料修改發生時都會寫入AOF檔案。
appendfsync everysec   #每秒鐘同步一次,該政策為AOF的預設政策。
           

AOF 可以做到全程持久化,隻需要在配置中開啟 appendonly yes。這樣 Redis 每執行一個修改資料的指令,都會把它添加到 AOF 檔案中,當 Redis 重新開機時,将會讀取 AOF 檔案進行重放,恢複到 Redis 關閉前的最後時刻。

使用 AOF 的優點是會讓 Redis 變得非常耐久。可以設定不同的 Fsync 政策,AOF的預設政策是每秒鐘 Fsync 一次,在這種配置下,就算發生故障停機,也最多丢失一秒鐘的資料。

缺點是對于相同的資料集來說,AOF 的檔案體積通常要大于 RDB 檔案的體積。根據所使用的 Fsync 政策,AOF 的速度可能會慢于 RDB。

15、RDB與AOF應該如何選擇?

如果你非常關心你的資料,但仍然可以承受數分鐘内的資料丢失,那麼可以額隻使用 RDB 持久。

AOF 将 Redis 執行的每一條指令追加到磁盤中,處理巨大的寫入會降低Redis的性能,不知道你是否可以接受。

資料庫備份和災難恢複:定時生成 RDB 快照非常便于進行資料庫備份,并且 RDB 恢複資料集的速度也要比 AOF 恢複的速度快。

當然了,Redis 支援同時開啟 RDB 和 AOF,系統重新開機後,Redis 會優先使用 AOF 來恢複資料,這樣丢失的資料會最少。

16、你能說說 Redis 主從複制的過程和原理嗎?

主從配置結合哨兵模式能解決單點故障問題,提高 Redis 可用性。

從節點僅提供讀操作,主節點提供寫操作。對于讀多寫少的狀況,可給主節點配置多個從節點,進而提高響應效率。

Redis 單節點存在單點故障問題,為了解決單點問題,一般都需要對 Redis 配置從節點,然後使用哨兵來監聽主節點的存活狀态,如果主節點挂掉,從節點能繼續提供緩存功能,

關于複制過程,是這樣的:

  • 從節點執行 slaveof[masterIP][masterPort],儲存主節點資訊。
  • 從節點中的定時任務發現主節點資訊,建立和主節點的 Socket 連接配接。
  • 從節點發送 Ping 信号,主節點傳回 Pong,兩邊能互相通信。
  • 連接配接建立後,主節點将所有資料發送給從節點(資料同步)。
  • 主節點把目前的資料同步給從節點後,便完成了複制的建立過程。接下來,主節點就會持續的把寫指令發送給從節點,保證主從資料一緻性。

17、Reids主從資料同步的過程

Redis 2.8 之前使用 sync[runId][offset] 同步指令,Redis 2.8 之後使用 psync[runId][offset] 指令。

兩者不同在于,Sync 指令僅支援全量複制過程,Psync 支援全量和部分複制。

介紹同步之前,先介紹幾個概念:

  • runId:每個 Redis 節點啟動都會生成唯一的 uuid,每次 Redis 重新開機後,runId 都會發生變化。
  • offset:主節點和從節點都各自維護自己的主從複制偏移量 offset,當主節點有寫入指令時,offset=offset+指令的位元組長度。

    從節點在收到主節點發送的指令後,也會增加自己的 offset,并把自己的 offset 發送給主節點。

    這樣,主節點同時儲存自己的 offset 和從節點的 offset,通過對比 offset 來判斷主從節點資料是否一緻。

  • repl_backlog_size:儲存在主節點上的一個固定長度的先進先出隊列,預設大小是 1MB。

主節點發送資料給從節點過程中,主節點還會進行一些寫操作,這時候的資料存儲在複制緩沖區中。

從節點同步主節點資料完成後,主節點将緩沖區的資料繼續發送給從節點,用于部分複制。

主節點響應寫指令時,不但會把命名發送給從節點,還會寫入複制積壓緩沖區,用于複制指令丢失的資料補救。

硬核!Redis 面試知識點總結,建議收藏!

上面是 Psync 的執行流程,從節點發送 psync[runId][offset] 指令,主節點有三種響應:

  • FULLRESYNC:第一次連接配接,進行全量複制
  • CONTINUE:進行部分複制
  • ERR:不支援 psync 指令,進行全量複制

18、能具體說下全量複制和部分複制的過程嗎?

硬核!Redis 面試知識點總結,建議收藏!

上面是全量複制的流程。主要有以下幾步:

  • 從節點發送 psync ? -1 指令(因為第一次發送,不知道主節點的 runId,是以為?,因為是第一次複制,是以 offset=-1)。
  • 主節點發現從節點是第一次複制,傳回 FULLRESYNC {runId} {offset},runId 是主節點的 runId,offset 是主節點目前的 offset。
  • 從節點接收主節點資訊後,儲存到 info 中。
  • 主節點在發送 FULLRESYNC 後,啟動 bgsave 指令,生成 RDB 檔案(資料持久化)。
  • 主節點發送 RDB 檔案給從節點。到從節點加載資料完成這段期間主節點的寫指令放入緩沖區。
  • 從節點清理自己的資料庫資料。
  • 從節點加載 RDB 檔案,将資料儲存到自己的資料庫中。如果從節點開啟了 AOF,從節點會異步重寫 AOF 檔案。

關于部分複制有以下幾點說明:

①部分複制主要是 Redis 針對全量複制的過高開銷做出的一種優化措施,使用 psync[runId][offset] 指令實作。

當從節點正在複制主節點時,如果出現網絡閃斷或者指令丢失等異常情況時,從節點會向主節點要求補發丢失的指令資料,主節點的複制積壓緩沖區将這部分資料直接發送給從節點。

這樣就可以保持主從節點複制的一緻性。補發的這部分資料一般遠遠小于全量資料。

②主從連接配接中斷期間主節點依然響應指令,但因複制連接配接中斷指令無法發送給從節點,不過主節點内的複制積壓緩沖區依然可以儲存最近一段時間的寫指令資料。

③當主從連接配接恢複後,由于從節點之前儲存了自身已複制的偏移量和主節點的運作 ID。是以會把它們當做 psync 參數發送給主節點,要求進行部分複制。

④主節點接收到 psync 指令後首先核對參數 runId 是否與自身一緻,如果一緻,說明之前複制的是目前主節點。

之後根據參數 offset 在複制積壓緩沖區中查找,如果 offset 之後的資料存在,則對從節點發送+COUTINUE 指令,表示可以進行部分複制。因為緩沖區大小固定,若發生緩沖溢出,則進行全量複制。

⑤主節點根據偏移量把複制積壓緩沖區裡的資料發送給從節點,保證主從複制進入正常狀态。

19、主從複制會存在哪些問題呢?

主從複制會存在以下問題:

  • 一旦主節點當機,從節點晉升為主節點,同時需要修改應用方的主節點位址,還需要指令所有從節點去複制新的主節點,整個過程需要人工幹預。
  • 主節點的寫能力受到單機的限制。
  • 主節點的存儲能力受到單機的限制。
  • 原生複制的弊端在早期的版本中也會比較突出,比如:Redis 複制中斷後,從節點會發起 psync。

    此時如果同步不成功,則會進行全量同步,主庫執行全量備份的同時,可能會造成毫秒或秒級的卡頓。

20、怎麼解決主從複制存在的問題,哨兵的功能

當然是哨兵啊。

硬核!Redis 面試知識點總結,建議收藏!

如圖,是 Redis Sentinel(哨兵)的架構圖。Redis Sentinel(哨兵)主要功能包括主節點存活檢測、主從運作情況檢測、自動故障轉移、主從切換。

Redis Sentinel 最小配置是一主一從。Redis 的 Sentinel 系統可以用來管理多個 Redis 伺服器。

該系統可以執行以下四個任務:

  • 監控:不斷檢查主伺服器和從伺服器是否正常運作。
  • 通知:當被監控的某個 Redis 伺服器出現問題,Sentinel 通過 API 腳本向管理者或者其他應用程式發出通知。
  • 自動故障轉移:當主節點不能正常工作時,Sentinel 會開始一次自動的故障轉移操作,它會将與失效主節點是主從關系的其中一個從節點更新為新的主節點,并且将其他的從節點指向新的主節點,這樣人工幹預就可以免了。
  • 配置提供者:在 Redis Sentinel 模式下,用戶端應用在初始化時連接配接的是 Sentinel 節點集合,從中擷取主節點的資訊。

21、哨兵的工作原理

硬核!Redis 面試知識點總結,建議收藏!

①每個 Sentinel 節點都需要定期執行以下任務:每個 Sentinel 以每秒一次的頻率,向它所知的主伺服器、從伺服器以及其他的 Sentinel 執行個體發送一個 PING 指令。(如上圖)

硬核!Redis 面試知識點總結,建議收藏!

②如果一個執行個體距離最後一次有效回複 PING 指令的時間超過 down-after-milliseconds 所指定的值,那麼這個執行個體會被 Sentinel 标記為主觀下線。(如上圖)

硬核!Redis 面試知識點總結,建議收藏!

③如果一個主伺服器被标記為主觀下線,那麼正在監視這個伺服器的所有 Sentinel 節點,要以每秒一次的頻率确認主伺服器的确進入了主觀下線狀态。

硬核!Redis 面試知識點總結,建議收藏!

④如果一個主伺服器被标記為主觀下線,并且有足夠數量的 Sentinel(至少要達到配置檔案指定的數量)在指定的時間範圍内同意這一判斷,那麼這個主伺服器被标記為客觀下線。

硬核!Redis 面試知識點總結,建議收藏!

⑤一般情況下,每個 Sentinel 會以每 10 秒一次的頻率向它已知的所有主伺服器和從伺服器發送 INFO 指令。

當一個主伺服器被标記為客觀下線時,Sentinel 向下線主伺服器的所有從伺服器發送 INFO 指令的頻率,會從 10 秒一次改為每秒一次。

硬核!Redis 面試知識點總結,建議收藏!

⑥Sentinel 和其他 Sentinel 協商客觀下線的主節點的狀态,如果處于 SDOWN 狀态,則投票自動選出新的主節點,将剩餘從節點指向新的主節點進行資料複制。

硬核!Redis 面試知識點總結,建議收藏!

⑦當沒有足夠數量的 Sentinel 同意主伺服器下線時,主伺服器的客觀下線狀态就會被移除。

當主伺服器重新向 Sentinel 的 PING 指令傳回有效回複時,主伺服器的主觀下線狀态就會被移除。

總結

本文在一次面試的過程中講述了 Redis 是什麼,Redis 的特點和功能,Redis 緩存的使用,Redis 為什麼能這麼快,Redis 緩存的淘汰政策,持久化的兩種方式,Redis 高可用部分的主從複制和哨兵的基本原理。

隻要功夫深,鐵杵磨成針,平時準備好,面試不用慌。雖然面試不一定是這樣問的,但萬變不離其“宗”。

繼續閱讀