最通俗易懂的 Redis 架構模式詳解

前言

  

  話說有一名意大利程序員,在 2004 年到 2006 年間主要做嵌入式工作,之後接觸了 Web,2007 年和朋友共同創建了一個網站,並為了解決這個網站的負載問題(為了避免 MySQL 的低性能),於是親自定做一個數據庫,並於 2009 年開發完成,這個就是 Redis。這個意大利程序員就是 Salvatore Sanfilippo 江湖人稱 Redis 之父,大家更習慣稱呼他 Antirez。

  

  

  Redis 技術越來越火爆,其超高的性能,簡潔輕量的設計,易上手,分佈式架構的支持,在緩存等領域出色的表現造就了它現在的地位。

  官方的 Benchmark 數據:測試完成了 50 個並發執行 10W 個請求。設置和獲取的值是一個 256 位元組字符串。

  結果:讀的速度是 110000次/s,寫的速度是 81000次/s。

  為了滿足開發市場需求,Redis 支持單機主從哨兵集群多種架構模式,本文帶大家詳細講解這幾種架構模式的區別。

  

單機模式

  單機模式顧名思義就是安裝一個 Redis,啟動起來,業務調用即可。例如一些簡單的應用,並非必須保證高可用的情況下可以使用該模式。

  

優點

  

  • 部署簡單;
  • 成本低,無備用節點;
  • 高性能,單機不需要同步數據,數據天然一致性。

  

缺點

  

  • 可靠性保證不是很好,單節點有宕機的風險。
  • 單機高性能受限於 CPU 的處理能力,Redis 是單線程的。

  

  單機 Redis 能夠承載的 QPS(每秒查詢速率)大概在幾萬左右。取決於業務操作的複雜性,Lua 腳本複雜性就極高。假如是簡單的 key value 查詢那性能就會很高。

  假設上千萬、上億用戶同時訪問 Redis,QPS 達到 10 萬+。這些請求過來,單機 Redis 直接就掛了。系統的瓶頸就出現在 Redis 單機問題上,此時我們可以通過主從複製解決該問題,實現系統的高並發。

  

主從複製

  Redis 的複製(Replication)功能允許用戶根據一個 Redis 服務器來創建任意多個該服務器的複製品,其中被複制的服務器為主服務器(Master),而通過複製創建出來的複製品則為從服務器(Slave)。 只要主從服務器之間的網絡連接正常,主服務器就會將寫入自己的數據同步更新給從服務器,從而保證主從服務器的數據相同。

  數據的複製是單向的,只能由主節點到從節點,簡單理解就是從節點只支持讀操作,不允許寫操作。主要是讀高並發的場景下用主從架構。主從模式需要考慮的問題是:當 Master 節點宕機,需要選舉產生一個新的 Master 節點,從而保證服務的高可用性。

  

優點

  

  • Master/Slave 角色方便水平擴展,QPS 增加,增加 Slave 即可;
  • 降低 Master 讀壓力,轉交給 Slave 節點;
  • 主節點宕機,從節點作為主節點的備份可以隨時頂上繼續提供服務;

  

缺點

  

  • 可靠性保證不是很好,主節點故障便無法提供寫入服務;
  • 沒有解決主節點寫的壓力;
  • 數據冗餘(為了高並發、高可用和高性能,一般是允許有冗餘存在的);
  • 一旦主節點宕機,從節點晉陞成主節點,需要修改應用方的主節點地址,還需要命令所有從節點去複製新的主節點,整個過程需要人工干預;
  • 主節點的寫能力受到單機的限制;
  • 主節點的存儲能力受到單機的限制。

  

哨兵模式

  主從模式中,當主節點宕機之後,從節點是可以作為主節點頂上來繼續提供服務,但是需要修改應用方的主節點地址,還需要命令所有從節點去複製新的主節點,整個過程需要人工干預。

  於是,在 Redis 2.8 版本開始,引入了哨兵(Sentinel)這個概念,在主從複製的基礎上,哨兵實現了自動化故障恢復。如上圖所示,哨兵模式由兩部分組成,哨兵節點和數據節點:

  • 哨兵節點:哨兵節點是特殊的 Redis 節點,不存儲數據;
  • 數據節點:主節點和從節點都是數據節點。

  

  Redis Sentinel 是分佈式系統中監控 Redis 主從服務器,並提供主服務器下線時自動故障轉移功能的模式。其中三個特性為:

  • 監控(Monitoring):Sentinel 會不斷地檢查你的主服務器和從服務器是否運作正常;
  • 提醒(Notification):當被監控的某個 Redis 服務器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程序發送通知;
  • 自動故障遷移(Automatic failover):當一個主服務器不能正常工作時, Sentinel 會開始一次自動故障遷移操作。

  

  接下來我們了解一些 Sentinel 中的關鍵名詞,然後系統講解下哨兵模式的工作原理。

  

定時任務

  

  Sentinel 內部有 3 個定時任務,分別是:

  • 每 1 秒每個 Sentinel 對其他 Sentinel 和 Redis 節點執行 PING 操作(監控),這是一個心跳檢測,是失敗判定的依據。
  • 每 2 秒每個 Sentinel 通過 Master 節點的 channel 交換信息(Publish/Subscribe);
  • 每 10 秒每個 Sentinel 會對 Master 和 Slave 執行 INFO 命令,這個任務主要達到兩個目的:
    • 發現 Slave 節點;
    • 確認主從關係。

  

主觀下線

  

  所謂主觀下線(Subjectively Down, 簡稱 SDOWN)指的是單個 Sentinel 實例對服務器做出的下線判斷,即單個 Sentinel 認為某個服務下線(有可能是接收不到訂閱,之間的網絡不通等等原因)。

  主觀下線就是說如果服務器在給定的毫秒數之內, 沒有返回 Sentinel 發送的 PING 命令的回復, 或者返回一個錯誤, 那麼 Sentinel 會將這個服務器標記為主觀下線(SDOWN)。

  

客觀下線

  

  客觀下線(Objectively Down, 簡稱 ODOWN)指的是多個 Sentinel 實例在對同一個服務器做出 SDOWN 判斷,並且通過命令互相交流之後,得出的服務器下線判斷,然後開啟 failover。

  只有在足夠數量的 Sentinel 都將一個服務器標記為主觀下線之後, 服務器才會被標記為客觀下線(ODOWN)。只有當 Master 被認定為客觀下線時,才會發生故障遷移。

  

仲裁

  

  仲裁指的是配置文件中的 quorum 選項。某個 Sentinel 先將 Master 節點標記為主觀下線,然後會將這個判定通過 sentinel is-master-down-by-addr 命令詢問其他 Sentinel 節點是否也同樣認為該 addr 的 Master 節點要做主觀下線。最後當達成這一共識的 Sentinel 個數達到前面說的 quorum 設置的值時,該 Master 節點會被認定為客觀下線並進行故障轉移。

  quorum 的值一般設置為 Sentinel 個數的二分之一加 1,例如 3 個 Sentinel 就設置為 2。

  

哨兵模式工作原理

  

  1. 每個 Sentinel 以每秒一次的頻率向它所知的 Master,Slave 以及其他 Sentinel 節點發送一個 PING 命令;
  2. 如果一個實例(instance)距離最後一次有效回復 PING 命令的時間超過配置文件 own-after-milliseconds 選項所指定的值,則這個實例會被 Sentinel 標記為主觀下線
  3. 如果一個 Master 被標記為主觀下線,那麼正在監視這個 Master 的所有 Sentinel 要以每秒一次的頻率確認 Master 是否真的進入主觀下線狀態;
  4. 當有足夠數量的 Sentinel(大於等於配置文件指定的值)在指定的時間範圍內確認 Master 的確進入了主觀下線狀態,則 Master 會被標記為客觀下線
  5. 如果 Master 處於 ODOWN 狀態,則投票自動選出新的主節點。將剩餘的從節點指向新的主節點繼續進行數據複製;
  6. 在正常情況下,每個 Sentinel 會以每 10 秒一次的頻率向它已知的所有 Master,Slave 發送 INFO 命令;當 Master 被 Sentinel 標記為客觀下線時,Sentinel 向已下線的 Master 的所有 Slave 發送 INFO 命令的頻率會從 10 秒一次改為每秒一次;
  7. 若沒有足夠數量的 Sentinel 同意 Master 已經下線,Master 的客觀下線狀態就會被移除。若 Master 重新向 Sentinel 的 PING 命令返回有效回復,Master 的主觀下線狀態就會被移除。

  

優點

  

  • 哨兵模式是基於主從模式的,所有主從的優點,哨兵模式都有;
  • 主從可以自動切換,系統更健壯,可用性更高;
  • Sentinel 會不斷地檢查你的主服務器和從服務器是否運作正常。當被監控的某個 Redis 服務器出現問題時, Sentinel 可以通過 API 向管理員或者其他應用程序發送通知。

  

缺點

  

  • 主從切換需要時間,會丟失數據;

  • 還是沒有解決主節點寫的壓力;

  • 主節點的寫能力,存儲能力受到單機的限制;

  • 動態擴容困難複雜,對於集群,容量達到上限時在線擴容會變得很複雜。

  

集群模式

  

  假設上千萬、上億用戶同時訪問 Redis,QPS 達到 10 萬+。這些請求過來,單機 Redis 直接就掛了。系統的瓶頸就出現在 Redis 單機問題上,此時我們可以通過主從複製解決該問題,實現系統的高並發。

  主從模式中,當主節點宕機之後,從節點是可以作為主節點頂上來繼續提供服務,但是需要修改應用方的主節點地址,還需要命令所有從節點去複製新的主節點,整個過程需要人工干預。於是,在 Redis 2.8 版本開始,引入了哨兵(Sentinel)這個概念,在主從複製的基礎上,哨兵實現了自動化故障恢復

  哨兵模式中,單個節點的寫能力,存儲能力受到單機的限制,動態擴容困難複雜。於是,Redis 3.0 版本正式推出 Redis Cluster 集群模式,有效地解決了 Redis 分佈式方面的需求。Redis Cluster 集群模式具有高可用可擴展性分佈式容錯等特性。

  

  

  Redis Cluster 採用無中心結構,每個節點都可以保存數據和整個集群狀態,每個節點都和其他所有節點連接。Cluster 一般由多個節點組成,節點數量至少為 6 個才能保證組成完整高可用的集群,其中三個為主節點,三個為從節點。三個主節點會分配槽,處理客戶端的命令請求,而從節點可用在主節點故障後,頂替主節點。

  如上圖所示,該集群中包含 6 個 Redis 節點,3 主 3 從,分別為 M1,M2,M3,S1,S2,S3。除了主從 Redis 節點之間進行數據複製外,所有 Redis 節點之間採用 Gossip 協議進行通信,交換維護節點元數據信息。

  總結下來就是:讀請求分配給 Slave 節點,寫請求分配給 Master,數據同步從 Master 到 Slave 節點。

  

分片

  

  單機、主從、哨兵的模式數據都是存儲在一個節點上,其他節點進行數據的複製。而單個節點存儲是存在上限的,集群模式就是把數據進行分片存儲,當一個分片數據達到上限的時候,還可以分成多個分片。

  Redis Cluster 採用虛擬哈希槽分區,所有的鍵根據哈希函數映射到 0 ~ 16383 整數槽內,計算公式:HASH_SLOT = CRC16(key) & 16384。每一個節點負責維護一部分槽以及槽所映射的鍵值數據。

  Redis Cluster 提供了靈活的節點擴容和縮容方案。在不影響集群對外服務的情況下,可以為集群添加節點進行擴容也可以下線部分節點進行縮容。可以說,槽是 Redis Cluster 管理數據的基本單位,集群伸縮就是槽和數據在節點之間的移動。

  簡單的理解就是:擴容或縮容以後,槽需要重新分配,數據也需要重新遷移,但是服務不需要下線。

  

  假如,這裡有 3 個節點的集群環境如下:

  • 節點 A 哈希槽範圍為 0 ~ 5500;
  • 節點 B 哈希槽範圍為 5501 ~ 11000;
  • 節點 C 哈希槽範圍為 11001 ~ 16383。

  此時,我們如果要存儲數據,按照 Redis Cluster 哈希槽的算法,假設結果是: CRC16(key) & 16384 = 6782。 那麼就會把這個key 的存儲分配到 B 節點。此時連接 A、B、C 任何一個節點獲取 key,都會這樣計算,最終通過 B 節點獲取數據。

  假如這時我們新增一個節點 D,Redis Cluster 會從各個節點中拿取一部分 Slot 到 D 上,比如會變成這樣:

  • 節點 A 哈希槽範圍為 1266 ~ 5500;
  • 節點 B 哈希槽範圍為 6827 ~ 11000;
  • 節點 C 哈希槽範圍為 12288 ~ 16383;
  • 節點 D 哈希槽範圍為 0 ~ 1265,5501 ~ 6826,11001 ~ 12287

  這種特性允許在集群中輕鬆地添加和刪除節點。同樣的如果我想刪除節點 D,只需要將節點 D 的哈希槽移動到其他節點,當節點是空時,便可完全將它從集群中移除。

  

主從模式

  

  Redis Cluster 為了保證數據的高可用性,加入了主從模式,一個主節點對應一個或多個從節點,主節點提供數據存取,從節點複製主節點數據備份,當這個主節點掛掉後,就會通過這個主節點的從節點選取一個來充當主節點,從而保證集群的高可用。

  回到剛才的例子中,集群有 A、B、C 三個主節點,如果這 3 個節點都沒有對應的從節點,如果 B 掛掉了,則集群將無法繼續,因為我們不再有辦法為 5501 ~ 11000 範圍內的哈希槽提供服務。

  所以我們在創建集群的時候,一定要為每個主節點都添加對應的從節點。比如,集群包含主節點 A、B、C,以及從節點 A1、B1、C1,那麼即使 B 掛掉系統也可以繼續正確工作。

  因為 B1 節點屬於 B 節點的子節點,所以 Redis 集群將會選擇 B1 節點作為新的主節點,集群將會繼續正確地提供服務。當 B 重新開啟後,它就會變成 B1 的從節點。但是請注意,如果節點 B 和 B1 同時掛掉,Redis Cluster 就無法繼續正確地提供服務了。

  

優點

  

  • 無中心架構;
  • 可擴展性,數據按照 Slot 存儲分佈在多個節點,節點間數據共享,節點可動態添加或刪除,可動態調整數據分佈;
  • 高可用性,部分節點不可用時,集群仍可用。通過增加 Slave 做備份數據副本。
  • 實現故障自動 failover,節點之間通過 gossip 協議交換狀態信息,用投票機制完成 Slave 到 Master 的角色提升。

  

缺點

  

  • 數據通過異步複製,無法保證數據強一致性
  • 集群環境搭建複雜,不過基於 Docker 的搭建方案會相對簡單。

  

總結

  

  隨着互聯網的飛速發展,我們享受着技術帶來的便利,同時也給從業者帶來了如何保證項目高並發、低延時的技術挑戰。Redis 以其超高的性能,簡潔輕量的設計,易上手,分佈式架構的支持,在緩存等領域出色的表現等,得到了業界廣泛的關注和應用,在當今高性能架構中,也發揮着越來越重要的作用。甚至可以說,Redis 已經成為 IT 互聯網大型系統的標配,熟練掌握 Redis 成為開發、運維人員的必備技能。

  如果不深挖底層,僅僅只是從使用的角度出發,Redis 的學習成本將會非常低。如果作為一個很好的中間件去研究的話,還是有很多值得學習和借鑒的地方。以上幾種模式,每種都有各自的優缺點,在實際場景中要根據業務特點去選擇合適的模式使用。

  

參考資料

  

本文採用 知識共享「署名-非商業性使用-禁止演繹 4.0 國際」許可協議

大家可以通過 分類 查看更多關於 Redis 的文章。

  

🤗 您的點贊轉發是對我最大的支持。

📢 掃碼關注 哈嘍沃德先生「文檔 + 視頻」每篇文章都配有專門視頻講解,學習更輕鬆噢 ~