Redis 面試必備知識點

驚卻一目發表於2021-05-05

1、Redis 簡介

  • 是一個用 C 語言開發的,高效能的鍵值對資料庫。
  • 資料存在於記憶體,讀寫速度快。
  • 可用來做快取、分散式鎖、訊息佇列。
  • 提供多種資料型別來支援不同的業務場景。
  • 支援事務、持久化、Lua 指令碼、多種叢集方案。

2、Redis 與 Memcached 對比

共同點:

  • 都是基於記憶體的資料庫,常用來做快取。
  • 都有過期策略。
  • 效能都非常高。

區別:

  • Redis 支援多種資料型別;而 Memcached 只支援 string。
  • Redis 支援資料持久化;而 Memcached 不支援。
  • Redis 有災難恢復機制(通過載入持久化資料實現);而 Memcached 沒有。
  • Redis 記憶體不足時,可以將不用的資料放到磁碟上;而 Memcached 會直接報異常。
  • Redis 原生支援叢集模式;而 Memcached 沒有原生的叢集模式,需要客戶端實現。
  • Redis 使用單執行緒、IO 多路複用的網路模型(Redis 6.0 引入了多執行緒 IO);而 Memcached 使用多執行緒、非阻塞 IO 複用的網路模式。
  • Redis 支援釋出訂閱模型、Lua 指令碼、事務等功能;而 Memcached 不支援。
  • Redis 過期資料的刪除策略使用了惰性刪除和定期刪除;而 Memcached 只用了惰性刪除。

3、為什麼要用 Redis

  • 高效能:Redis 的資料存在於記憶體,讀寫速度快。
  • 高併發:單機情況下,MySQL 這類資料庫 QPS 在 1w 左右(4 核 8G),而 Redis 可達到 10w+,甚至 30w+。

4、Redis 資料型別

  • string:簡單動態字串(SDS)。
    • 常用命令:set、get、strlen、decr、incr、setex 等。
    • 應用場景:需要計數的場景,比如使用者訪問量、文章轉發量等。
  • list:雙向連結串列。
    • 常用命令:lpush、rpop、rpush、lpop、lrange、llen 等。
    • 應用場景:釋出訂閱(即訊息佇列)。
  • hash:類似 Java 的 HashMap,底層為陣列 + 連結串列。
    • 常用命令:hset、hget、hexists、hkeys、hvals 等
    • 應用場景:儲存物件資料。
  • set:類似 Java 的 HashSet,無序、去重,可實現交集、並集、差集的操作。
    • 常用命令:sadd、spop、smembers、sismember、scard、sunion 等。
    • 應用場景:需要存放的資料不能重複、需要獲取多個資料來源的交集、並集、差集。
  • sorted set(zset):和 set 相比,增加了一個權重引數 score,能夠按 score 進行排序。類似 Java 的 TreeSet,有序、去重。
    • 常用命令:zadd、zcard、zscore、zrange、zrevrange 等。
    • 應用場景:需要根據某個權重進行排序的場景,比如直播系統中的禮物排行榜、訊息彈幕等。

5、Redis 單執行緒模型

  • Redis 基於 Reactor 模式來開發自己的網路事件處理器,這個處理器被稱為檔案事件處理器。
  • 由於檔案事件處理器是單執行緒的,所以我們一般都說 Redis 是單執行緒模型。
  • Redis 通過 IO 多路複用來監聽多個客戶端連線(或者說監聽多個 socket)。
  • 檔案事件處理器主要包含四個部分:
    • 多個 socket:客戶端連線。
    • IO 多路複用程式:支援多個客戶端連線的關鍵。
    • 檔案事件分派器:將 socket 關聯到相應的事件處理器。
    • 事件處理器:連線應答處理器、命令請求處理器、命令回覆處理器。

6、Redis 單執行緒為什麼那麼快

  • 資料存在於記憶體,讀寫速度快。
  • 單執行緒操作,避免了死鎖、上下文切換帶來的效能開銷。
  • 採用非阻塞 IO 多路複用機制。

7、Redis 6.0 之前為什麼使用單執行緒

  • 單執行緒程式設計容易,並且更容易維護。
  • Redis 的效能瓶頸不在 CPU,主要在記憶體和網路。
  • 多執行緒存在死鎖、執行緒上下文切換等問題,甚至會影響效能。

8、Redis 6.0 之後為何引入了多執行緒

  • 為了提高網路 IO 讀寫效能。
  • Redis 只在網路資料讀寫這類耗時操作上使用了多執行緒,執行命令仍然是單執行緒順序執行,因此不需要擔心執行緒安全問題。

9、Redis 如何判斷資料是否過期

  • 通過過期字典(hash 表)儲存資料過期時間。
  • 過期字典的鍵指向 Redis 中的某個鍵,過期字典的值儲存了資料的過期時間(毫秒精度的時間戳)。

10、Redis 過期資料的刪除策略

  • 定時刪除:設定過期時間時,同時建立一個定時器,定時器會在過期時間到來時自動刪除過期資料。節約記憶體,但 CPU 壓力大。
  • 定期刪除:每隔一段時間抽取一批資料進行過期檢查。記憶體、CPU 壓力都不是很大。
  • 惰性刪除:在取出資料時進行過期檢查。節約 CPU 效能、但記憶體壓力大。

Redis 採用定期刪除 + 惰性刪除。但仍會漏掉某些過期資料,可能導致大量過期資料堆積在記憶體,從而導致記憶體溢位,Redis 通過記憶體淘汰機制來解決這個問題。

11、Redis 記憶體淘汰機制

  • volatile-lru(least recently used):從已設定過期時間的資料中,淘汰最近最少使用的資料。
  • volatile-ttl:從已設定過期時間的資料中,淘汰將要過期的資料。
  • volatile-random:從已設定過期時間的資料中,隨機淘汰資料。
  • allkeys-lru(least recently used):從所有資料中,淘汰最近最少使用的資料。
  • allkeys-random:從所有資料中,隨機淘汰資料。
  • noeviction:禁止淘汰資料,記憶體不足時直接報錯。

4.0 版本後增加兩種:

  • volatile-lfu(least frequently used):從已設定過期時間的資料中,淘汰最不經常使用的資料。
  • allkeys-lfu(least frequently used):從所有資料中,淘汰最不經常使用的資料。

12、Redis 持久化機制

  • RDB 持久化:預設方式,將某個時刻記憶體中的資料以快照的形式儲存在磁碟上。檔案小、恢復速度快,對效能影響小,但是實時性差。
  • AOF 持久化:以日誌的方式將每一條寫命令儲存到磁碟的檔案上。檔案大、恢復速度慢,對效能影響大,但是實時性高。目前 Redis 持久化的主流方式。
    • 一般設定為每秒同步一次 AOF 檔案:appendfsync 選項設定為 everysec。
    • AOF 重寫(bgrewriteaof 命令):將記憶體中的資料以命令的方式儲存到新的 AOF 檔案中,然後用新的 AOF 檔案替換舊的 AOF 檔案。

13、Redis 事務

  • 相關命令:
    • MULTI:開啟事務。
    • EXEC:執行事務中的所有命令。
    • DISCARD:取消事務,放棄執行事務中的所有命令。
    • WATCH:監聽一個或多個 key,如果事務在執行前,監聽的 key 被其他命令修改,則中斷事務,不會執行事務中的任何命令。
    • UNWATCH:取消 WATCH 對所有 key 的監聽。
  • 使用 MULTI 命令後可輸入多個命令,Redis 不會立即執行這些命令,而是會放到佇列中,等呼叫 EXEC 命令後再原子化執行這些命令。
  • Redis 不支援回滾,因此不滿足原子性。
  • 當 Redis 執行在 AOF 持久化模式下,並且 appendfsync 選項設為 always 時,具有永續性。

14、Redis 叢集(多機)

  • 主從模式:主庫(master)負責讀寫,從庫(slave)負責讀。不具備高可用。
    • master 掛了,不影響 slave,只是不再提供寫服務。
    • master 掛了,不會在 slave 中選一個 master。
  • 哨兵(Sentinel)模式:通過 sentinel 程式監視 master、slave 的執行狀態。
    • 哨兵模式是建立在主從模式的基礎上。
    • 為了避免 sentinel 掛掉,一般會做 sentinel 叢集。並且不和 master、slave 部署在同一臺機器。
    • sentinel 會每秒向 master、slave 以及其他 sentinel 例項傳送一個 PING 命令,以監視其執行狀態。
    • master 掛了,sentinel 會在 slave 中選一個 master,並修改所有例項的配置。
    • master 重啟後,將作為 slave 執行。
  • 叢集(Cluster)模式:Redis 提供的分散式資料庫方案。該模式是為了解決單機 Redis 容量上限的問題,它通過分片來進行資料共享。
    • 由多個節點組成,每個節點包含一主一從(或一主多從),從庫僅作為備用。
    • 客戶端可以連線任一節點的主庫進行讀寫。
    • 叢集的整個資料庫被分為 16384 個槽(slot),當儲存一個 key-value 時,會被分配在某個槽上。

15、快取穿透

  • 大量請求不存在快取中的 key,導致請求落在資料庫上。
  • 解決辦法:
    • 攔截非法 key:攔截所有一定不存在資料的 key。一般採用布隆過濾器。
    • 快取空值:即使查詢結果為空,也將這個空結果快取,但設定一個較短的過期時間。

16、快取雪崩

  • 大量快取在同一時間失效,或者一些被大量訪問的快取(熱點快取)在某一時刻失效,導致大量請求落在資料庫上。
  • 解決辦法:
    • 設定不同的過期時間。
    • 限流,避免同時處理大量請求。
    • 設定熱點快取永不失效。

交流區

Redis 面試必備知識點
微信公眾號:驚卻一目
個人部落格:驚卻一目

Reference

[1] https://snailclimb.gitee.io/javaguide-interview/#/./docs/d-2-redis
[2] https://blog.csdn.net/miss1181248983/article/details/90056960
[3] 《Redis 設計與實現》

相關文章