本文爲分佈式Redis深度歷險系列的第三篇,主要內容爲Redis的Cluster,也就是Redis集羣功能。java
Redis集羣是Redis官方提供的分佈式方案,整個集羣經過將全部數據分紅16384個槽來進行數據共享。node
一個集羣由多個Redis節點組成,不一樣的節點經過CLUSTER MEET
命令進行鏈接:git
CLUSTER MEET <ip> <port>
github
收到命令的節點會與命令中指定的目標節點進行握手,握手成功後目標節點會加入到集羣中,看個例子,圖片來自於Redis的設計與實現:redis
一個集羣的全部數據被分爲16384個槽,能夠經過CLUSTER ADDSLOTS
命令將槽指派給對應的節點。當全部的槽都有節點負責時,集羣處於上線狀態,不然處於下線狀態不對外提供服務。算法
clusterNode的位數組slots表明一個節點負責的槽信息。數組
struct clusterNode { unsigned char slots[16384/8]; /* slots handled by this node */ int numslots; /* Number of slots handled by this node */ ... }
看個例子,下圖中一、三、五、八、九、10位的值爲1,表明該節點負責槽一、三、五、八、九、10。緩存
每一個Redis Server上都有一個ClusterState的對象,表明了該Server所在集羣的信息,其中字段slots記錄了集羣中全部節點負責的槽信息。服務器
typedef struct clusterState { // 負責處理各個槽的節點 // 例如 slots[i] = clusterNode_A 表示槽 i 由節點 A 處理 // slots[i] = null 表明該槽目前沒有節點負責 clusterNode *slots[REDIS_CLUSTER_SLOTS]; }
能夠經過redis-trib工具對槽從新分配,重分配的實現步驟以下:網絡
CLUSTER GETKEYSINSLOT <slot> <count>
從源節點獲取最多count個槽slot的keyMIGRATE <target_ip> <target_port> <key_name> 0 <timeout>
命令,將被選中的鍵原子的從源節點遷移至目標節點。在槽重分配的過程當中,槽中的一部分數據保存着源節點,另外一部分保存在目標節點。這時若是要客戶端向源節點發送一個命令,且相關數據在一個正在遷移槽中,源節點處理步驟如圖:
當客戶端收到一個ASK錯誤的時候,會根據返回的信息向目標節點從新發起一次請求。
ASK和MOVED的區別主要是ASK是一次性的,MOVED是永久性的,有點像Http協議中的301和302。
咱們來看cluster下一次命令的請求過程,假設執行命令 get testKey
getCRC16(key) & (16384 - 1)
,獲得槽信息後,會從一個緩存map中得到槽對應的redis server信息,若是能獲取到,則調到第4步slots
命令以得到整個集羣的槽分佈信息,而後跳轉到第2步重試命令以上步驟大體就是redis cluster下一次命令請求的過程,但忽略了一個細節,若是要查找的數據鎖所在的槽正在重分配怎麼辦?
集羣中每一個Redis節點都會按期的向集羣中的其餘節點發送PING消息,若是目標節點沒有在有效時間內回覆PONG消息,則會被標記爲疑似下線。同時將該信息發送給其餘節點。當一個集羣中有半數負責處理槽的主節點都將某個節點A標記爲疑似下線後,那麼A會被標記爲已下線,將A標記爲已下線的節點會將該信息發送給其餘節點。
好比說有A,B,C,D,E 5個主節點。E有F、G兩個從節點。
當E節點發生異常後,其餘節點發送給A的PING消息將不能獲得正常回復。當過了最大超時時間後,假設A,B先將E標記爲疑似下線;以後C也會將E標記爲疑似下線,這時C發現集羣中由3個節點(A、B、C)都將E標記爲疑似下線,超過集羣複製槽的主節點個數的一半(>2.5)則會將E標記爲已下線,並向集羣廣播E下線的消息。
當F、G(E的從節點)收到E被標記已下線的消息後,會根據Raft算法選舉出一個新的主節點,新的主節點會將E複製的全部槽指派給本身,而後向集羣廣播消息,通知其餘節點新的主節點信息。
選舉新的主節點算法與選舉Sentinel頭節點的過程很像:
最後,聊聊redis集羣的其餘兩種實現方案。
客戶端作路由,採用一致性hash算法,將key映射到對應的redis節點上。
其優勢是實現簡單,沒有引用其餘中間件。
缺點也很明顯:是一種靜態分片方案,擴容性差。
Jedis中的ShardedJedis是該方案的實現。
該方案在client與redis之間引入一個代理層。client的全部操做都發送給代理層,由代理層實現路由轉發給不一樣的redis服務器。
其優勢是: 路由規則可自定義,擴容方便。
缺點是: 代理層有單點問題,多一層轉發的網絡開銷
原文:Java架構筆記
免費Java高級資料須要本身領取,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo高併發分佈式等教程,一共30G。
傳送門: https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q