Redis 面試必備知識點

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、緩存雪崩

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

交流區


微信公衆號:驚卻一目
個人博客:驚卻一目

Reference

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章