Redis 可以透過主從節點讀寫分離來擴充套件主節點讀取能力,那麼新增主節點也可以增強資料寫入能力。
https://zhuanlan.zhihu.com/p/104641341
https://www.cnblogs.com/lemon-flm/p/15190528.html
Redis Cluster
Redis 透過主從複製來擴充套件主節點的資料讀取能力,但是當主節點的寫入能力成為瓶頸時,叢集就是增強 Redis 效能的解決方案之一。
Redis Cluster
透過主從複製實現讀操作的高可用,透過 Gossip協議
實現叢集狀態資料及路由資料的管理,透過 法團協議
完成故障檢測(至少 N/2+1 個哨兵節點均認為主節點故障,才客觀認為該主節點故障),透過 RAFT協議
實現故障轉移(Raft選舉出主哨兵節點來主持新 Leader 的替換)。
叢集原理
針對 Redis Master 叢集擴充套件,我們需要關注以下問題:
- 新資料寫入,應該寫哪臺機器;
- 查詢時應該查哪臺機器;
- 新節點加入,如何重新分配資料,讓新節點對外提供服務;節點退出,如何把節點上儲存的資料分配到其它機器;
- 有節點故障,如何保證外部仍然能夠讀取該節點上的資料,而不至於崩潰。
資料分佈問題
Redis 使用了虛擬桶分割槽,利用特定的雜湊函式將資料分佈到多個桶中。具體點,Redis 利用 CRC16
函式計算雜湊值並將所有鍵打散到 16384 個桶中,每個桶只可以由一個 Master 節點儲存,一個 Master 節點可以儲存多個桶資料。
資料查詢問題
我們需要根據 Key 計算出具體的儲存桶的位置,常規的方法是透過中間層代理。由一箇中間節點(HDFS中 NameNode)管理所有後設資料,但是中間層的單點性會導致高併發時瓶頸。
Redis 沒有采用代理方法,而是客戶端直練每個節點。每個節點儲存著整個叢集狀態,該狀態中包含了每個桶的負責節點。Redis 內部用一個大小固定的 CLUSTER_SLOTS
來儲存每個桶的負責節點,Redis 接收關於某個 Key 的查詢請求,先計算出桶的編號,如果桶由當前節點負責,直接查詢,否則回覆 MOVED
重定向錯誤,通知客戶端真正的正確節點。
為了避免多次重定向查詢,可以將桶的資訊儲存到客戶端,這樣客戶端計算出桶就可以直接查詢到,如果叢集發生擴縮容導致桶儲存位置變化,只需要在訪問時 MOVED
重定向並更新快取即可。
叢集伸縮
透過上述講解,我們知道 Redis 中資料是以 Slot 的形式存在,所以伸縮的本質就是 Slot 在不同節點間的遷移,那麼有以下問題:如果A節點正在向B節點遷移 Slot1 的資料,未完成遷移時可能出現資料共存在 A、B 兩個節點,該路由給哪個節點?
假設這樣一個場景:原本有 6379、6380、6381、6382 四個節點,現在加入 6383 節點,那麼其它節點需要向該節點遷移 slots ,Redis 並沒有實現自動負載均衡工具,需要使用者決定如何遷移。
設定遷入遷出狀態
現在假設 6379 資料向 6383 遷移。
- 每個 Redis 叢集節點都會儲存
clusterState
,它儲存了整個叢集中 slots 和其儲存節點的對應關係。那麼當 6379 遷移 slot1 時,首先標記當前處於IMGRATING
狀態,6382 同樣標記自己處於IMPORTING
,具體實現上就是分別設定migrating_slots_to
、importing_slots_from
兩個陣列的對應 index 值。在未完成遷移前,叢集中所有節點都會將 slot1 的請求重定向到 6379 節點; - 6379 將 slot1 標記為
IMGRATING
狀態時,會接受所有 slot1 的請求,但只會處理桶在自己節點的請求,否則重定向到 6382 節點; - 6382 節點將 slot1 標記為
IMPORTING
狀態時,也可以接受 slot1 的請求,但前提是該請求中必須包含重定向命令(也就是說6379無法處理轉發過來的),否則轉發到 6379 節點處理。
如上過程就解決了以下問題:
- 遷移過程中,其它節點收到請求後都重定向到 6379 節點(
IMGTATING
狀態); - 6379 收到請求判斷能否處理,不能處理重定向到 6382(
IMPORTING
); - 6382 節點只接收並處理標記有重定向標誌的請求,否則重定向到 6379。
桶遷移完畢後,廣播更新 slot1桶所儲存的節點資訊。