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 設計與實現》