1. 程式人生 > 實用技巧 >[2020] Redis 最新面試題

[2020] Redis 最新面試題

Redis 的資料型別(資料結構)

  1. string (二進位制安全,可以儲存任意型別的資料)
  2. list(連結串列)
  3. 字典(就是hashmap
  4. set(不重複無序的hashmap)
  5. zset(按照給定的 score 排序的 set)
  6. HyperLogLog(來做基數統計的演算法,簡介
  7. Geo(支援地理位置的操作,使用簡介
  8. Pub/Sub
  9. BloomFilter
  10. RedisSearch
  11. Redis-ML

快取雪崩(快取擊穿)

他們出現的原理都是訪問快取的時候,key 剛好失效,導致直接訪問 DB,壓垮後臺。

解決辦法就是

  1. 讓 key 的過期時間分散開,不要集中失效
  2. 使用鎖或者訊息佇列的方式

分散式鎖

使用 setnx 命令後為了防止死鎖,需要對 key 施加 expire 命令,防止死鎖,但是存在執行 expire 命令前宕機,造成死鎖的發生。

解決辦法就是使用複雜的 setnx 命令,他可以把 setnx 和 expire 一起原子執行

如何尋找有固定字首的 key

使用 KEYS pattern 命令,如:KEYS alib*

但是因為 Redis 是單執行緒的,執行該命令後會導致 Redis 阻塞住。

解決辦法就是使用 scan 命令,scan 命令可以無阻塞的提取出指定模式的 key 列表,但是會有一定的重複概率,在客戶端做一次去重就可以了,但是整體所花費的時間會比直接用 keys指令長

如何用 Redis 做非同步佇列

使用 list 資料結構,在一邊加入另一邊取出,若取出來的是 null,則消費執行緒應該 sleep,或者消費執行緒不使用 lpop rpop 命令,改為 blpop 或者 brpop 命令,若沒有元素可取,它會阻塞列表直到等待超時或發現可彈出元素為止。

如何生產一次,消費多次

使用釋出訂閱模式

但是在消費者下線的情況下,生產的訊息會丟失,得使用專業的訊息佇列如 rabbitmq

Redis 如何實現延時佇列

使用 zset ,用時間戳作為 score,訊息會按照時間順序排序

然後使用 zrangebyscore key min max [WITHSCORES] [LIMIT offset count]

來取出比當前時間小的 key 的 value

持久化

Redis 4.0 時代以 RDB 為主,AOF 只記錄上一次 RDB 到現在的更改記錄

開啟混合持久化:aof-use-rdb-preamble yes

工作原理:其實還是一種 AOF 機制,但是新增了 RDB 的特性,先看此模式下的 AOF 的資料結構圖

  • 看圖就知道混合模式指的就是重寫 AOF 的時候,將此刻記憶體裡面的資料做成 RDB,在此過程中增量的資料寫入到緩衝區,最終形成新的 aof 檔案。接著刪除舊的 AOF
  • 重啟恢復時先恢復 RDB,再重放新增的 AOF 指令
  • AOF:記錄每一次的寫操作到日誌上,重啟時重放日誌以重建資料
    • 每隔一段時間呼叫系統的 fsync 函式強制將 os cache 裡面的資料重新整理到磁碟上
  • RDB:每隔一段時間儲存一次當前時間點上的資料快照
    • 快照就是一次又一次地從頭開始創造一切,全量的

持久化如何工作的

關鍵詞:寫時複製 fork 子程序

  • 每當 Redis 需要轉儲資料集到磁碟時,會發生:
    • Redis 呼叫 fork()。於是我們有了父子兩個程序。
    • 子程序開始將資料集寫入一個臨時 RDB / AOF 檔案。
    • 當子程序完成了新 RDB 檔案,替換掉舊檔案。
    • AOF 的 fork(),與 RDB 不同的是父程序會在一個記憶體緩衝區中積累新的變更,同時將新的變更寫入新的 AOF 檔案,所以即使重寫失敗我們也安全。當子程序完成重寫檔案,父程序收到一個訊號,追加記憶體緩衝區到子程序建立的檔案末尾,接著自動重新命名檔案為新的,然後開始追加新資料到新檔案
  • 這個方法可以讓 Redis 獲益於寫時複製(copy-on-write)機制

AOF 為什麼要重寫

AOF 記錄的是 Redis 的每一次變更,這個變更包含了大量的冗餘操作,導致 AOF 體積變大,恢復緩慢。

通過重寫這個體積大的 AOF 檔案,可以實現新的 AOF 檔案不會包含任何浪費空間的冗餘命令,通常體積會較舊 AOF 檔案小很多。

Pipeline

將多個指令一起傳送,減少 IO,提高吞吐量

Redis 的同步機制

Redis 可以使用主從同步,從從同步

第一次同步時,主節點做一次 bgsave,並同時將後續修改操作記錄到記憶體 buffer,待完成後將 RDB 檔案全量同步到複製節點,複製節點接受完成後將 RDB 映象載入到記憶體

載入完成後,再通知主節點將期間修改的操作記錄同步到複製節點進行重放就完成了同步過程。後續的增量資料通過 AOF 日誌同步即可,有點類似資料庫的 binlog

Redis 叢集

Redis Sentinal 著眼於高可用,在 master 宕機時會自動將 slave 提升為 master,繼續提供服務。

Redis Cluster 著眼於擴充套件性,在單個 redis 記憶體不足時,使用 Cluster 進行分片儲存。

Redis 的樂觀鎖 Watch 是怎麼實現的

Watch 會在事務開始之前盯住 1 個或多個關鍵變數,如下圖:

當事務執行時,也就是伺服器收到了 exec 指令要順序執行快取的事務佇列時, Redis 會檢查關鍵變數自 Watch 之後,是否被修改了。

上圖顯示,watch abc 之後執行事務之前,執行了一次 incr 操作,所以在 exec 的時候失敗,watch 的實現原理不是 CAS 中的Cmpxchg指令,而是藉助 Redis 的單執行緒執行機制,採用了 watched_keys 的資料結構和序列流程實現了樂觀鎖,具體解釋就是:

每一個被 watch 的 key 都會被構造成一個 watched_keys 資料型別,多個被 watch 的 key 構造成連結串列儲存著

假設客戶端 A 和 B 都 watch abc
但是併發時 Redis Server 中只會有一個執行緒在執行,
當 A 修改了 watch 命令監視的 key 後,會改變 abc 的 watched_keys 的狀態為 dirty,
客戶端 B 會檢查這個被 watch 的 abc,發現他的狀態是 dirty 的時候就會終止事務

Redis 如何節省記憶體

關鍵詞:ziplistquicklist、物件共享

Ziplist是一個緊湊的資料結構,每一個元素之間都是連續的記憶體,如果在 Redis 中,Redis 啟用的資料結構資料量很小時,Redis 就會切換到使用緊湊儲存的形式來進行壓縮儲存。

Linkedlist 是 ziplist 的雙向連結串列版本,可以在兩端執行 push 和 pop 操作

物件共享:指的就是多個key的value是一樣的話,就把多個key指向同一個value即可,如下圖:

A和B都指向值100,則A、B 共用同一個100物件

Redis 的過期策略

  1. 定時刪除:建立一個定時器,讓定時器在鍵過期時來執行刪除
    1. 對CPU不友好
    2. 影響效能
  2. 定期刪除:每隔一段時間,程式都要對資料庫進行一次檢查,刪除裡面的過期鍵,至於要刪除多少過期鍵,由演算法而定。
    1. 要麼對 CPU 不友好
    2. 要麼對記憶體不友好
  3. 惰性刪除:get Key 的時候才檢查是否過期,過期了就刪除返回 null
    1. 對記憶體不友好
    2. 可能導致記憶體溢位

Redis 同步策略

最簡單的架構模式就是:一臺 master 和多個 slave

master 開啟持久化策略,負責寫入操作slave 只負責讀取操作

同步的目的就是為了【讀寫分離】【容災備份】

同步的過程:

  1. slave 傳送 SYNC 給 master
  2. master 接收到命令後一邊快取繼續寫入的命令,一邊 fork 子程序生成 RDB 檔案
  3. 子程序寫完 RDB 之後,父程序把 RDB 傳送給 slave,slave 接收 RDB 並重現資料
  4. 父程序增量地將快取的寫命令傳送給 slave

Redis 為什麼是單程序單執行緒的

注意:這裡的單執行緒指的是處理 I/O 事件是單執行緒的,併發的請求進入 Redis 後會排隊,只有上一個處理完了,才會繼續處理下一個。

注意:Redis 可不完全是單程序的,開啟持久化的時候,會fork 子程序完成 RDB/AOF 的建立