什么是Redis?
Redis 是一個開源的內存數據結構存儲系統,可用作數據庫、緩存和消息代理。它因其快速的性能、靈活性和易用性而得到廣泛應用。
Redis 數據存儲類型
Redis 允許開發人員以各種數據結構(例如字符串、位圖、位域、哈希、列表、集合、有序集合、地理空間數據、超日志和流)存儲、檢索和操作數據。
使用 Redis 的優勢
所有 Redis 數據都駐留在服務器的主內存中,這與 PostgreSQL、SQL Server 等將大部分數據存儲在磁盤上的數據庫不同。因此,Redis 可以支持更高數量級的操作和更快的響應時間。因此,它實現了超快的性能,平均讀寫操作時間不到毫秒,從而支持每秒數百萬次的操作。
Redis 提供了一個完整的系統,它為我們提供了兩種架構(單片機和分布式)的緩存系統,從而提高了數據檢索速度,因為通過內存中的鍵(例如哈希表)直接訪問操作將降低從原始 SQL 數據庫讀取數據的整體復雜性。
Redis 的工作架構
Redis 架構有多種,具體取決于用例和規模:
1. 單 Redis 實例
這是最直接的 Redis 部署方式。它要求用戶設置并運行小型實例,以幫助他們擴展服務并加速其發展。然而,它也有其自身的缺點,因為如果正在運行的實例崩潰或不可用,所有對 Redis 的調用都會失敗。因此,系統的整體性能和速度會下降。
單 Redis 實例
2. Redis HA(高可用性)
另一種常見的設置是主部署加上一個始終與副本保持同步的輔助部署。輔助實例在我們的部署中可以是一個或多個實例,這有助于擴展從 Redis 的讀取,并在主實例丟失的情況下提供故障轉移。
Redis HA(輔助實例故障轉移)
3. Redis Sentinel
Sentinel 對應于分布式系統。它的設計理念是,一個哨兵進程集群協同工作,協調狀態,從而為 Redis 系統提供持續的可用性。哨兵進程的職責如下:
- 監控:確保主實例和輔助實例按預期運行。
- 通知:將 Redis 實例中發生的事件通知所有系統管理員。
- 故障期間管理:如果主實例長時間不可用,并且有足夠多的節點確認故障,哨兵節點可以在故障期間啟動一個進程。
Redis 哨兵
4. Redis 集群/Redis 集群主從模型:Redis 的終極架構
Redis 集群是 Redis 的終極架構。它允許 Redis 進行水平擴展。
在 Redis 集群中,我們決定將存儲的數據分散到多臺機器上,這稱為分片。因此,集群中的每個 Redis 實例都被視為整個數據的一個分片。
Redis 集群使用算法分片。為了找到給定鍵對應的分片,我們會對鍵進行哈希運算,并將結果除以分片數量。然后,使用確定性哈希函數(即給定鍵始終映射到同一個分片),我們可以推斷出將來讀取特定鍵時該鍵的位置。
系統設計中的 Redis 集群架構
為了處理系統中分片的進一步添加(重新分片),Redis 集群使用哈希槽 (Hashslot),所有數據都映射到該哈希槽。因此,當我們添加新的分片時,我們只需將哈希槽從一個分片移動到另一個分片,從而簡化了向集群添加新主實例的過程。這樣做的好處是,無需停機,并且性能影響最小。我們來看下面的示例:
假設哈希槽的數量為 10K。
實例 1 包含從 0 到 5000 的哈希槽,
實例 2 包含從 5001 到 10000 的哈希槽。
現在,假設我們需要添加另一個實例,那么哈希槽的分布如下:
實例 1 包含從 0 到 3333 的哈希槽。
實例 2 包含從 3334 到 6667 的哈希槽。
實例 3 包含從 6668 到 10000 的哈希槽。
Redis 集群中的 gossiping 是什么?
為了確定整個集群的健康狀況,Redis 集群使用 gossiping。在下面的示例中,我們有 3 個主實例和 3 個輔助節點。所有這些節點都會不斷確定哪些節點當前可用于處理請求。假設有足夠多的分片一致認為實例 1 沒有響應,它們就可以將實例 1 的輔助節點提升為主節點,以保持集群的健康。一般來說,為了實現最強健和容錯的網絡,主節點數量必須為奇數,并且每個節點有兩個副本。
Redis 持久化模型的類型
Redis 提供了兩種主要的持久化選項來將數據保存到磁盤:RDB 和 AOF。這兩種選項各有優缺點,具體使用哪種取決于應用程序的具體需求。以下列出了幾種持久化選項:
1. RDB(實時數據庫)持久化模型:
RDB 是 Redis 數據集的即時快照,以二進制文件的形式存儲。RDB 文件包含數據集在特定時間點的表示形式,可用于在服務器崩潰或重啟時恢復數據集。由于 RDB 采用二進制格式存儲數據,因此在磁盤空間利用率和性能方面非常高效。
RDB 可以配置為定期保存數據,或者根據某些條件(例如最小寫入操作次數)保存數據。然而,RDB 的缺點是,如果服務器在按計劃創建 RDB 快照之前崩潰,則可能導致數據丟失。
RDB 中的快照
快照是 Redis 持久化過程中的一個過程,它會在內存中創建整個數據集的時間點快照,并將其以二進制格式保存到磁盤。此快照可用于在服務器崩潰或重啟時恢復數據集。Redis 通過其 RDB 持久化機制支持快照。
快照的工作原理如下:
- Redis 從父進程派生出一個子進程。
- 子進程在內存中創建數據集當前狀態的副本。
- 子進程將數據集副本寫入臨時 RDB 文件。
- 子進程將臨時文件重命名為最終的 RDB 文件名,并覆蓋所有現有的 RDB 文件。
- 子進程終止,Redis 繼續處理請求。
Redis 可以配置為定期自動執行快照,或根據某些條件(例如最小寫入操作次數或自上次快照以來經過的最短時間)執行快照。如果我們正在進行繁重的工作并更改大量鍵,那么每分鐘將生成一個快照;如果更改相對較少,則每 5 分鐘生成一個快照;如果更改更少,則每 15 分鐘生成一個快照。
RDB(實時數據庫)的優勢
- RDB 文件非常適合備份,因為它是 Redis 數據非常緊湊的單文件時間點表示。它使我們能夠在發生災難時輕松恢復數據集的不同版本。
- 它非常適合災難恢復,因為它是一個可以傳輸到遠程數據中心的單個緊湊文件。
RDB(實時數據庫)的劣勢
現在讓我們比較一下 Redis DB 的劣勢:
- RDB 并非最佳選擇。
- 如果我們需要最大限度地降低 Redis 停止工作時數據丟失的可能性,
- 我們可以配置生成 RDB 的不同保存點。但是,我們通常每五分鐘或更長時間就會創建一個 RDB 快照,因此,如果 Redis 由于任何原因而未正確關閉而停止工作,我們應該準備好丟失最新幾分鐘的數據。
2. AOF(僅追加文件)持久化模型
AOF 將所有寫入操作以人類可讀的格式記錄到一個文件中。該文件包含自上次保存以來對數據集執行的所有寫入操作的記錄,以便在發生崩潰時重建數據集。AOF 比 RDB 具有更好的持久性,因為它將每個寫入操作都記錄到磁盤。
AOF 可以配置為定期保存數據,或根據某些條件(例如最小寫入操作次數)保存數據。但是,由于 AOF 會將每個寫入操作都記錄到磁盤,因此會導致性能下降和磁盤空間占用增加。
由于快照的持久性較差,因此僅追加文件是 Redis 的另一種完全持久化策略。
可以通過以下方式在配置文件中啟用 AOF:
appendonly yes
AOF 的工作原理?
- Redis 會從父進程 fork 一個子進程。
- 子進程會在內存中創建數據集當前狀態的副本。
- 子進程將數據集的副本寫入臨時文件中的新 AOF 文件。
- 父進程將所有新的更改累積到內存緩沖區中(但同時,它會將新的更改寫入舊的僅追加文件,因此如果重寫失敗,我們是安全的)。
- 當子進程完成文件重寫后,父進程會收到信號,并將內存緩沖區中的更改追加到子進程生成的文件末尾。
- 然后,Redis 會自動將舊文件重命名為新文件,并開始將新數據追加到新文件中。
AOF 的優勢
- Redis 的 AOF 日志更加持久,因為我們可以采用不同的 fsync 策略,例如完全不 fsync、每秒 fsync 或每次查詢時 fsync。
- 它是一個僅追加的日志,因此不會出現尋道,也不會在斷電時出現損壞問題。
- 如果日志由于磁盤已滿或其他原因突然結束,Redis 的檢查工具可以自動修復任何寫入一半的命令。
AOF 的缺點
現在讓我們比較一下 AOF 的缺點:
- 對于相同的數據集,這些文件通常比等效的 RDB 文件更大。
- 根據具體的 fsync 策略,它可能比 RDB 慢。
- AOF 可以提高數據一致性,但不能保證數據完全一致。數據丟失的可能性很小,但考慮到 RDB 速度更快,丟失數據的可能性比 RDB 模式要小。
選擇哪個——實時數據庫 (RDB) 還是追加文件 (AOF)?
通常的思路是,如果我們想要獲得與 PostgreSQL 等普通數據庫相當的數據安全性,我們應該同時使用這兩種持久化方法。如果我們非常重視數據,但又能忍受災難發生時幾分鐘的數據丟失,那么我們可以只使用 RDB。
3. 無持久化模型
Redis 還提供了完全禁用持久化的選項,在這種情況下,數據僅存儲在內存中。當 Redis 用作緩存時,此選項非常有用,因為數據丟失后可以重新生成。
4. 混合 (RDB+AOF) 持久化模型
Redis 提供了同時使用 RDB 和 AOF 持久化的選項,這被稱為混合持久化。此選項兼具 RDB 和 AOF 的優點,因為 AOF 日志用于在重啟后重放寫入操作,而 RDB 快照用于在特定時間點恢復數據集。
Redis 中的可用性、一致性和分區
以下是 Redis 如何處理可用性、一致性和分區的簡要概述:
- 可用性:Redis 使用主從復制模型來確保高可用性。這意味著有一個“主”節點接受所有寫入操作,還有多個“從”節點實時從主節點復制數據。如果主節點發生故障,可以將其中一個從節點提升為新的主節點。
- 一致性:Redis 為單鍵操作提供強一致性保證,這意味著如果將值寫入某個鍵,集群中的任何節點都可以立即讀取該值。但是,Redis 不為多鍵操作提供事務一致性,這意味著某些節點可能看到與其他節點不同的數據視圖。
- 分區:Redis 支持分片,允許將數據集分區到多個節點。Redis 使用基于哈希的分區方案,其中每個鍵根據其哈希值分配給特定節點。Redis 還提供了一種在集群中添加或刪除節點時重新分配數據的機制。
我們可以使用 Redis 來替代原始數據庫嗎?
基于以上討論,Redis 似乎是原始數據庫的更好選擇,因為它提供了更快的檢索速度。即便如此,Redis 也不會被用作系統中數據庫的主要選項。
Redis 應該始終作為提升整體系統性能的第二支撐,因為根據 CAP 定理,Redis 既不具備一致性,也不具備高可用性。
這是因為,如果服務器崩潰,我們將丟失內存中的所有數據。崩潰時丟失這些數據是可以接受的,但對于某些其他應用來說,在服務器重啟后立即重新加載 Redis 數據就變得非常重要。
結論
總的來說,Redis 是一個強大的系統設計工具,但它可能并不適合所有用例。在決定是否在特定應用程序中使用 Redis 時,務必仔細考慮其局限性。