1. 程式人生 > >去中心Redis-Cluster規範(三)-重定向和重雜湊

去中心Redis-Cluster規範(三)-重定向和重雜湊

去中心Redis-Cluster規範(三)

本文翻譯自官方文件

重定向和重雜湊

MOVED重定向

Redis客戶端可以自由想叢集內的任何節點(包括從節點)傳送查詢指令.收到指令的節點會分析查詢指令,如果指令可接受(查詢指令中只有一個key,或者多個屬於相同hash slot的key)會查詢指令中的key隸屬的hash slot由哪個節點負責.

如果hash slot由當前節點服務,則查詢指令可以被簡單的處理掉,否則檢查hash slot到節點的對映表,回覆客戶端一個MOVED錯誤資訊,像下面示例這樣.

GET x
-MOVED 3999 127.0.0.1:6381

這個錯誤資訊包含key所屬的hash slot(3999)和可以處理該查詢指令的服務例項的ip:port.客戶端需要重新發起查詢到這個指定的節點ip地址和埠.注意如果客戶端在重新發起查詢之前等待了一段時間,並且同事叢集配置發生變更,hash slot 3999現在由另外的節點提供服務,目標節點會再次回覆MOVED錯誤資訊.相同的情況也會發生在通訊的節點沒有收到最新的配置資訊時.

從叢集的視角看都是使用ID作為節點標識,但是為了簡化客戶端介面,僅向客戶端暴露hash slot到ip:port的對映,從客戶端的視角看redis節點是使用ip:port作為標識的.

雖然不強制,但是客戶端應該記住hash slot 3999由127.0.0.1:6381提供服務.一旦需要發起一個新命令,客戶端能夠計算目標key的hash slot,並且有更高的機率選擇正確的節點.

另外一個可選方案是在收到MOVED重定向資訊時使用CLUSTER NODESCLUSTER SLOTS命令,直接重新整理儲存在客戶端的整個叢集佈局資訊.

當發生重定向時,通常是多個slots被重新配置而不僅僅是一個,所以儘快更新整個客戶端的配置資訊是最佳策略.

注意當叢集穩定時(沒有正在執行的配置變更操作),最終所有客戶端會獲得hash slots -> nodes的對映關係,令叢集更高效,因為客戶端可以直接定位正確的節點,不需要重定向也沒有代理或其他的單點失效問題.

一個完整有效的客戶必須處理 -ASK 重定向.文件後面的內容會詳細說明.

叢集線上重配置

Redis-Cluster支援在執行期間增減節點.新增或移除節點被抽象為相同的操作:從一個節點移動hash slot到另外一個節點.這意味著相同的基本機制可以用來重新平衡叢集,新增或刪除節點,等等.

  • 要向叢集新增一個新節點,首先新增一個空節點到叢集,然後從已有節點移動一部分hash slot到這個新節點.
  • 從叢集刪除一個節點,需要將分配在這個節點上的hash slot移動到其它已有的節點.
  • 重平衡叢集就是在叢集節點間移動一部分hash slot.

實現的核心是四處移動hash slot的能力.hash slot僅僅是一組key的集合,所以在重雜湊期間Redis-Cluster真正要做的就是將key從一個例項移動到另一個例項.移動一個hash slot也就是移動所有分配到這個hash slot中的key.

要理解這是如何工作的,我們需要展示一下在Redis-Cluster中用來維護slots遷移表的CLUSTER子命令.可以使用如下這些子命令:

前兩個命令 ADDSLOTS和DELSLOTS,可以簡單的用來向一個Redis節點新增或移除slots.分配一個slot意味著告知指定的主節點他將要負責儲存和服務指定的hash slot內容.

在hash slots被分配之後,他們會使用流言協議傳播給整個叢集.在後面的配置傳播章節中會詳細說明.

ADDSLOTS命令通常在從頭建立一個新叢集時用來為每個主節點分配16384個hash slot中的一部分.

DELSLOTS主要用於人工修改叢集配置或出去除錯目的:實踐中很少使用.

SETSLOT自命令,如果使用SETSLOT \

CLUSTER GETKEYSINSLOT slot count

上面的命令會返回指定的hash slot中的count個key.對每個返回的key,redis-trib傳送給節點A一個MIGRATE命令,會以自動的方式將指定的key從A遷移到B(為了在沒有競爭的條件下遷移key,兩個例項需要同事被鎖住一段時間,通常是非常少的時間片).命令示例如下:

MIGRATE target_host target_port key target_database id timeout

MIGRATE 會連線目標例項,傳送key的序列化版本,一旦收到了OK,舊key會被從它自己的資料集中刪掉.從外部客戶端的視角看在任何時間,key要麼在A上要麼在B上.

在Redis-Cluster中沒有必要來指定0意外的資料庫(database),但是MIGRATE是個通用命令可能會用於其他非Redis-Cluster環境中的其他任務.即使是在遷移像長列表這樣的複雜key時,雖然MIGRATE被優化為儘可能的快,但是如果使用資料庫的應用有比較高的延遲限制,在Redis-Cluster中執行含有大key的叢集重配置是不明智的.

當遷移過程最終完成後,SETSLOT <slot> NODE <node-id>命令會被髮送到遷移過程涉及到的兩個節點來將slots的狀態重新設定為正常.相同的命令還會被髮送到其它的節點以避免等待正常的叢集新配置傳播.

ASK重定向

在之前的章節中我們簡單的談到了ASK重定向.為什麼我不能簡單的使用MOVED重定向?因為MOVED表示我們認為要訪問的hash slot永久的由另外的節點提供服務接下來的所有請求應該直接訪問指定的節點,ASK表示僅下一次請求訪問指定節點.

因為下一次有關hash slot 8的請求可能使用了一個依然在A中的key,所以我們總是想要客戶端先嚐試訪問A如果需要的話再訪問B.由於這種情況僅會針對16384個solt中的一個發生,所以對叢集的效能影響是可以接受的.

我們需要強制客戶端行為,以確保客戶端總是在嘗試訪問A節點之後在嘗試訪問B節點,如果客戶端在傳送一個查詢請求前先發送了ASKING命令,節點B金輝接受被設定為IMPORTING的slot的查詢請求.

本質上來說ASKING命令在客戶端設定了一個一次性標記強制一個節點提供對IMPORTING的slot的服務.

從客戶端的視角來看,ASK重定向的語義如下:

  • 如果接收到了ASK重定向,僅傳送一次請求到重定向的指定節點但是接下來的請求依然傳送到舊節點.
  • 傳送被重定向的請求前先發送一個ASKING命令.
  • 不要更新客戶端的本地對映表,將slot 8 對映到B.

一旦hash slot 8 遷移完成,A將傳送MOVED訊息,客戶端可以永久的將hash slot 8對映到新的ip:port.注意如果客戶端有bug,提前設定了這個對映關係也沒有問題.因為這個客戶端在發起請求前,不會發送ASKING命令,所以B會使用MOVED錯誤訊息將客戶端重定向到A.

在 CLUSTER SETSLOT 命令的文件中對Slots遷移也使用了類似的術語來解釋了slots遷移.但是為了減少冗餘使用了不同的措辭.

客戶端首次連線和處理重定向處理

客戶端實現可以不在記憶體中儲存slots配置(slot編號和提供服務的節點地址的對映關係)而僅僅通過隨機訪問一個節點然後等待重定向的方式工作.但是這樣的客戶端實現是非常低效的.

Redis-Cluster客戶端應該足夠聰慧可以記住slots配置.這個配置不是必須保持實時更新的.因為訪問一個錯誤節點僅僅會導致簡單的重定向,而這個重定向會出發客戶端檢視的更新.

客戶端通常需要在以下兩種場景獲取一份完整的slots和對應節點地址列表:

  • 在啟動時初始化slots配置
  • 接收到MOVED重定向訊息

注意客戶端可以接收到MOVED重定向訊息時可以只在對映表中更新移動了的slot,然而這樣通常是低效的,因為搜歌slots的配置修改經常是同時發生的(例如一個從節點升級為主節點,所有的被就的主節點服務的slots會被重對映).所以收到MOVED重定向後重新獲取一份slots到節點的對映關係表是更簡單的方案.

為了獲取slots配置,除了CLUSTER NODES之外,Redis-Cluster提供了另外一個命令,這個命令不去要解析並且僅僅提供了客戶端嚴格需要的資訊.

這個心命令是CLUSTER SLOTS,它提供了一個slots範圍的陣列和服務這些slot範圍的主從節點.

127.0.0.1:7000> cluster slots
1) 1) (integer) 5461
   2) (integer) 10922
   3) 1) "127.0.0.1"
      2) (integer) 7001
   4) 1) "127.0.0.1"
      2) (integer) 7004
2) 1) (integer) 0
   2) (integer) 5460
   3) 1) "127.0.0.1"
      2) (integer) 7000
   4) 1) "127.0.0.1"
      2) (integer) 7003
3) 1) (integer) 10923
   2) (integer) 16383
   3) 1) "127.0.0.1"
      2) (integer) 7002
   4) 1) "127.0.0.1"
      2) (integer) 7005

在返回的陣列中開頭的兩個子元素是slots範圍的開始和結束.其他附加的元素表示ip:port對.第一個ip:port對是服務這個slot範圍的主節點,然後其餘的ip:port對是所有服務這個slot範圍的從節點(不處於錯誤狀態下,沒有被設定FAIL標記).

例如輸出中的第一個元素表示slot範圍從5461到10922被服務於127.0.0.1:7001,並且可以擴充套件只讀負載到127.0.0.1:7004這個從節點.

如果叢集缺少配置,CLUSTER SLOTS不能確保返回的slots範圍覆蓋了整個16384個slots,所以客戶端應該初始化slots配置,使用NULL來填充目標節點,並且當用戶試圖使用隸屬於未分配的slot的key執行命令時需要報告一個錯誤.

發現一個slot沒有被分配而返回錯誤訊息給呼叫者前,客戶端應該再次嘗試獲取slots配置來檢查現在叢集是否被正確配置了.

多key操作

使用hash tags,客戶端可以自由使用多key操作.如下的操作是有效的:

MSET {user:1000}.name Angela {user:1000}.surname White

當key所屬的slot正在進行重雜湊時,多key操作可能變為不可用.即使在重雜湊進行時,如果所有目標key都存在於相同的節點上(源節點或目標節點),這樣的key操作依然可用.

在重雜湊進行時,如果操作的key被分隔在源和目標節點上,會生成一個-TRYAGAIN錯誤資訊.客戶端可以過一會兒重試操作,或者直接報錯.

一旦指定的hash slot遷移結束,所有在這個slot中的多key操作將重新可用.

使用從節點擴充套件讀能力

對於一個指定的命令,正常情況下從節點會將客戶端重定向到牽扯到的slot所在的主節點.然後客戶端也可以通過READONLY命令使用從節點來擴充套件讀能力.

READONLY告知Redis-Cluster的一個從節點客戶端可以接受讀到稍舊的資料,且不會執行寫操作.

當連線處於readonly模式,叢集只會在操作牽扯到的key不在所訪問的從節點的主節點上時重定向客戶端.原因如下:

  1. 客戶端傳送的命令涉及到的slots從來沒有存在於該從節點的主節點上.
  2. 叢集被重配置(例如重雜湊)且從節點不在服務命令涉及到的hash slot.

當發生上述情況,客戶端應該想之前的章節解釋的那樣,更新自己的hash slot對映關係.

連線的readonly狀態可以使用READWRITE清除掉.