Redis—叢集擴縮容

Stitches發表於2024-04-06

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 叢集擴充套件,我們需要關注以下問題:

  • 新資料寫入,應該寫哪臺機器;
  • 查詢時應該查哪臺機器;
  • 新節點加入,如何重新分配資料,讓新節點對外提供服務;節點退出,如何把節點上儲存的資料分配到其它機器;
  • 有節點故障,如何保證外部仍然能夠讀取該節點上的資料,而不至於崩潰。

資料分佈問題

img

Redis 使用了虛擬桶分割槽,利用特定的雜湊函式將資料分佈到多個桶中。具體點,Redis 利用 CRC16 函式計算雜湊值並將所有鍵打散到 16384 個桶中,每個桶只可以由一個 Master 節點儲存,一個 Master 節點可以儲存多個桶資料。

資料查詢問題

img

我們需要根據 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_toimporting_slots_from 兩個陣列的對應 index 值。在未完成遷移前,叢集中所有節點都會將 slot1 的請求重定向到 6379 節點;
  • 6379 將 slot1 標記為 IMGRATING 狀態時,會接受所有 slot1 的請求,但只會處理桶在自己節點的請求,否則重定向到 6382 節點;
  • 6382 節點將 slot1 標記為 IMPORTING 狀態時,也可以接受 slot1 的請求,但前提是該請求中必須包含重定向命令(也就是說6379無法處理轉發過來的),否則轉發到 6379 節點處理。

如上過程就解決了以下問題:

  • 遷移過程中,其它節點收到請求後都重定向到 6379 節點(IMGTATING 狀態);
  • 6379 收到請求判斷能否處理,不能處理重定向到 6382(IMPORTING);
  • 6382 節點只接收並處理標記有重定向標誌的請求,否則重定向到 6379。

桶遷移完畢後,廣播更新 slot1桶所儲存的節點資訊。

相關文章