redis cluster模式
通過前幾章,我們已經瞭解redis從單機版、高併發的主從架構和保證高可用的哨兵模式。但是主從架構的高併發是針對那些讀遠大於寫的場景,如果要保證讀和寫都是高併發的場景呢?那就是可以使用redis叢集模式。
節點
redis叢集模式通常是有多個redis節點組成。Redis伺服器在啟動時會根據cluster-enabled配置選項是否為yes來決定是否開啟伺服器的叢集模式。
# cluster-enabled yes
cluster-enabled yes
當啟動叢集模式後,在叢集中的每個節點都會存在一個clusterState資料結構。這個結構記錄了在當前節點的視角下,叢集目前所處的狀態,例如叢集是線上還是下線,叢集包含多少個節點,叢集當前的配置紀元等資訊。如下圖為node1的clusterState結構。
槽指派
在一個三個redis節點組成的redis叢集中,當我們往該叢集寫入一個命令,那這個命令會被寫入到哪個節點呢?
redis叢集將整個資料庫分為16384個槽(slot)。每個redis節點負責其中一部分的槽,當16384個槽都有redis節點負責時,整個redis叢集才能正常工作。
為啥是16384個槽(slot)
所以往redis叢集寫入一個命令後,當前節點通過CRC16(key)和&12384計算出一個位於1~16384之間的值,該值決定這對鍵值對屬於哪個槽。
命令寫入
在redis每一個節點中都儲存在這個一個關於redis叢集的clusterState結構,這個結構包含這個當前這個叢集的相關資訊,其中也包含著所有槽的指派資訊。
如上圖所示,在一個三個redis節點的叢集中,每一個redis節點都存在這clusterState資訊。ClusterState結構中有slots屬性,並指向一個大小為16384的redisNode陣列。
命令寫入的流程:
首先寫入其中一個節點,該節點開始通過槽分配演算法計算槽值M。 在slots屬性中,redisNode[m]快速定位到該槽負責的節點。 判斷負責的節點是否為自己,是執行命令,否返回moved命令(moved : )。該命令包含正確的節點的ip和埠號。 客戶端接收到moved的錯位資訊,將命令傳送到正確的節點。
槽維護
在起初所有槽沒有被指派時,可以通過執行以下命令將一個或者多個槽指派當前執行該命令的redis節點。
#CLUSTERMEET<slot>[slot...]
CLUSTERMEET0,1,2,3
槽資訊會被儲存在redisnode節點結構的slots和numslot屬性中,其中slots是一個二進位制陣列大小為16383,該陣列的索引正好對應16384個槽,值為0或1,1就表示該索引對應槽由當前redis節點負責;numslot記錄當前redis節點負責的槽總數。
在一個叢集的中redis節點會相互網路連線傳送資訊,並告知對方自己在負責哪些槽。其他redis節點收到資訊後,會更新自己的clusterState結構資訊,主要是包含全部槽資訊的clusterState.slots屬性和clusterState.nodes中對應節點的clusterNode.slots和numslot資訊。因此每一個redis節點中都儲存在完整的槽節點指派資訊。
重新指派
在三個節點的redis叢集,當要給該叢集在增加一個節點node4時,槽在已被全部分配,但是node4需要被分配一部分槽給他,不然node4相當於沒有工作,所以需要重新分片。以node3需要把槽15000~16384重新分配給node4為例:
開始對15000槽重新分配。 node4準備匯入屬於15000槽的鍵值對。 node3準備遷移屬於15000槽的鍵值對。 如果node3存在15000槽的鍵值對,將這些鍵值對匯入node4。 將槽指派給node4,完成對15000槽的重新指派。 其他槽的重新指派重複以上步驟。
當15555槽正在從node3轉移到node4時,會出現一種情況就是15555槽對應的鍵值對有一部分在node3,另一部分在node4。當客戶端需要對15555槽下的一個鍵值對的更新時,node3會首先檢查該鍵值對的key是否在當前節點,在就更新,不在返回ask錯信資訊,並指引客戶端將該鍵值對寫入node4。
複製和故障轉移
在redis叢集存在主從節點,主節點負責處理槽,從節點負責複製主節點以及當主節點下線時,替代下線的主節點繼續處理槽。
複製
可以通過一下命令讓接受該命令的節點成為node_id所指節點的從節點。
clusterreplicate<node_id>
執行該命令的流程:
接收到該命令的節點首先會在自己的clusterState.nodes字典中找到node_id所對應節點的clusterNode結構,並將自己的clusterState.myself.slaveof指標指向這個結構,以此來記錄這個節點正在複製的主節點。 修改自己的clusterNode屬性flags為REDIS_NODE_SLAVE和saveof屬相指向主節點clusterNode1。 從節點呼叫複製程式碼,複製主節點。
一個節點的成為從節點,並開始複製主節點的資訊會被告知其他所有所有節點,並去更新所有節點的clusterState中該主節點對應clusterNode結構的slaves和numslaves屬性。
故障轉移
叢集中的每個節點互相ping其他節點,當沒有收到有效回覆,就會認定其節點下線。當叢集中有一半的節點認定該節點下線,就會廣播一條訊息告知全部節點,將該節點認定為下線狀態,並開始故障轉移。
故障轉移步驟:
在下線主節點的所有從節點裡面選擇一個從節點。 被選中的從節點會執行SLAVEOF no one命令,成為新的主節點。 新的主節點會撤銷所有對已下線主節點的槽指派,並將這些槽全部指派給自己。 新的主節點向叢集廣播一條PONG訊息,這條PONG訊息可以讓叢集中的其他節點立即知道這個節點已經由從節點變成了主節點,並且這個主節點已經接管了原本由已下線節點負責處理的槽。 新的主節點開始接收和自己負責處理的槽有關的命令請求,故障轉移完成。
新的主節點選舉基本和在redis的Sentinel模式中選舉領頭Sentinel基本相似,就不在多做詳述可以翻開redis哨兵模式筆記。
參考:
redis設計與實現