集群

集群通過分片來進行數(shù)據(jù)共享谦疾,并提供復制和故障轉(zhuǎn)移

節(jié)點
  • 1.一個集群有多個節(jié)點
  • 2.節(jié)點代表的是master或者slave
  • 3.當節(jié)點A發(fā)生CLUSTER MEET <IP> <PORT>給節(jié)點B(ip和port都是B節(jié)點的地址),兩個節(jié)點就會進行握手當握手成功后恐锦。節(jié)點B就會被加入到節(jié)點A所在的集群。


    image.png

啟動節(jié)點

  • 1.redis服務器在啟動的時候會根據(jù)cluster-enabled配置選項是否為yes來決定是否開啟服務器的集群模式


    image.png

集群數(shù)據(jù)結(jié)構(gòu)

  • 1.clusterNode結(jié)構(gòu)保存了一個節(jié)點的當前狀態(tài)货岭,比如節(jié)點的創(chuàng)建時間逝她,節(jié)點名字局劲,節(jié)點當前的配置紀元,節(jié)點的ip地址和端口號
  • 2.每個節(jié)點都會使用一個clusterNode來記錄自己的狀態(tài)屈藐,并為集群中的所有其他節(jié)點(包括主從節(jié)點)
    創(chuàng)建一個相應的clusterNode結(jié)構(gòu)(這就意味著每個節(jié)點都保存了集群中所有的clusterNode榔组,這些節(jié)點都被存放在clusterstate中的nodes屬性中)


    image.png
  • 3.其中l(wèi)ink屬性是一個clusterLink結(jié)構(gòu),該結(jié)構(gòu)保存了連接點所需要的有關(guān)信息联逻,比如套字接描述符搓扯,輸入緩沖區(qū)和輸出緩沖區(qū)(應該保存的是A節(jié)點到B節(jié)點之間的鏈接)


    image.png
  • 4.redisclient和cluster都有自己的套字接描述符合輸入,輸出緩沖區(qū)包归。前者是用于連接客戶端的锨推,后者是用于連接節(jié)點的。
  • 5.clusterState記錄了在當前節(jié)點的視角下公壤,集群所處于的狀態(tài)换可,例如集群是在線還是下線,集群包含多少個節(jié)點厦幅,集群當前的配置紀元


    image.png

    image.png

cluster meet 命令的實現(xiàn)

  • 1.我們像A節(jié)點發(fā)生cluster meet命令沾鳄,A節(jié)點根據(jù)ip和port去尋找B節(jié)點并進行握手。握手的過程如下:
  • 2.A節(jié)點為B節(jié)點創(chuàng)建一個clusterNode結(jié)構(gòu)确憨,并放入A節(jié)點中的clusterState的nodes屬性(字典)
  • 3.之后A節(jié)點像B節(jié)點發(fā)生一個MEET消息(前面只是握手成功洞渔,這邊是發(fā)送消息),如果一切順利缚态,則B節(jié)點在接收到A節(jié)點發(fā)生的MEET消息后磁椒,節(jié)點B會為節(jié)點A創(chuàng)建一個clusterNode,并加入到B自己的clusterState的nodes屬性(字典)
  • 4.之后B向A返回一個PONG消息
  • 5.通過PONG消息,A知道了B已經(jīng)接收到自己的MEET消息玫芦,之后A將向節(jié)點B返回一條PING消息
  • 6.B收到PING消息后就是知道A已經(jīng)成功收到自己返回的PONG消息浆熔,握手完成


    image.png
  • 7.之后節(jié)點A會將節(jié)點B的信息通過GOSSIP協(xié)議傳播給集群中的其他節(jié)點,并讓其他節(jié)點與B進行握手桥帆。
槽指派
  • 1.集群的整個數(shù)據(jù)庫被分為16384個槽(slot)医增,這意味著假如你有10個節(jié)點,每個節(jié)點有10個數(shù)據(jù)庫老虫,那么就相當于每個數(shù)據(jù)庫只能分到20分之一的slot
  • 2.集群中的每個節(jié)點可以處理0個或者最多16384個slot
  • 3.如果16384個槽點都有節(jié)點在處理叶骨,則集群處于上線狀態(tài)(ok),否則只要有一個槽沒有被集群處理祈匙,那么集群處于下線狀態(tài)
  • 4.通過向節(jié)點發(fā)生cluster addslots命令忽刽,我們可以將一個或多個槽指派給節(jié)點負責

記錄節(jié)點的槽指派信息

  • 1.clusterNode結(jié)構(gòu)的slots屬性和numslot屬性記錄節(jié)點負責處理哪些槽


    image.png
  • 2.slots屬性一個二進制位數(shù)組天揖,這個數(shù)組的長度為16384/8=2048字節(jié)(因為是位數(shù)組所以需要除8得到字節(jié),1字節(jié)=8位)跪帝,也就是說是16384位
  • 3.redis以0為起始索引今膊,16383為終止索引,對slots數(shù)組中的16384個二進制位進行編號伞剑,并根據(jù)索引位上的值來判斷節(jié)點是否負責處理槽i
  • 4.如果slots數(shù)組在索引i上的二進制位的值為1斑唬,那么標識該節(jié)點負責處理槽i,如果為0代表該節(jié)點不負責處理槽i
  • 5.取出和設置slots數(shù)組中的任意一個二進制位的值的復雜度都是o(1)
  • 6.numslots 是記錄該節(jié)點負責處理的slot的數(shù)量

傳播節(jié)點的槽指派信息

  • 1.一個節(jié)點除了會將自己處理的槽記錄在clusterNode結(jié)構(gòu)的slots屬性和numslots屬性之外黎泣,它還會在將自己的slots數(shù)組通過消息發(fā)送給集群中的其他節(jié)點恕刘,以此來告知其他節(jié)點自己目前負責處理哪些槽。
  • 2.當節(jié)點收到其他節(jié)點發(fā)送來的slots屬性后抒倚,其會在custerState.nodes字典中查找對應節(jié)點的ClusterNode結(jié)構(gòu)雪营,并對結(jié)構(gòu)中的slots數(shù)組進行更新

記錄集群中所有槽的指派信息

  • 1.clusterState中的slots是clusterNode類型的


    image.png
  • 如果slots[i[指針指向Null代表槽未指向任何節(jié)點
  • 如果指向一個clusterNode,標識該槽已經(jīng)指派給了clusterNode結(jié)構(gòu)所代表的節(jié)點
  • 之所以還需要clusterState的slots是因為我們可以不需要遍歷就知道哪個槽分配給了哪個節(jié)點衡便,否則我們還需要遍歷clusterState中的nodes字典中的所有clusterNode結(jié)構(gòu)才能知道


    image.png

CLUSTER ADDSLOTS命令的實現(xiàn)

  • 該命令接受一個或多個槽作為參數(shù)献起,并將這些槽指派給接受該命令的節(jié)點負責
  • 具體步驟如下圖


    image.png
  • 下面圖先展示了 一個未指派的槽和以及如何指派槽1,2


    image.png

    image.png
在集群中執(zhí)行命令
  • 1.如果該槽正好就在該節(jié)點則直接指向,否則會返回MOVED錯誤
  • 2.客戶端根據(jù),MOVED錯誤會指引客戶轉(zhuǎn)向redirect至正確的節(jié)點镣陕,并再次執(zhí)行想要執(zhí)行的命令


    image.png

計算鍵屬于哪個槽(一共16384)

  • 計算的命令是CRC(16) &16383谴餐,用于計算CRC-16校驗和,而&16383語句則用于計算出一個介于0到16383直接的整數(shù)作為鍵KEY的槽號
  • 使用CLUSTER KEYSLOT <KEY> 可以查看一個給的的鍵屬于哪個槽


    image.png

判斷槽是否由當前節(jié)點負責處理

  • 當節(jié)點計算出鍵所屬的槽i之后呆抑,節(jié)點就會檢查自己在clusterState.slots數(shù)組中的項i岂嗓,判斷所在的槽是否由自己負責。
  • 如果槽i是當前節(jié)點負責鹊碍,那么直接執(zhí)行并返回給客戶端
  • 如果不屬于該節(jié)點厌殉,那么就會根據(jù)clusterstate.nodes[i]指向的節(jié)點的ip和port,然后像客戶端MOVED錯誤,指引客戶端轉(zhuǎn)向至正在處理槽i節(jié)點


    image.png

MOVED錯誤

  • MOVED <slot> <ip>:<port> 其中ip和port是正確的節(jié)點地址。
  • MOVED命令是由節(jié)點轉(zhuǎn)向客戶端
  • 一個集群的通常會與多個節(jié)點創(chuàng)建socket刑赶,所謂的節(jié)點轉(zhuǎn)向?qū)嶋H上就是換一個socket發(fā)送命令
  • 如果客戶端尚未與想要鏈接的節(jié)點創(chuàng)建socket盛垦,那么客戶端會現(xiàn)根據(jù)MOVED的指令 提供的IP和port來鏈接節(jié)點矫夯,然后再進行轉(zhuǎn)向。

節(jié)點數(shù)據(jù)庫的實現(xiàn)

  • 節(jié)點中 只能是用0號數(shù)據(jù)庫,而單機redis則沒有這個限制
  • 還使用clusterstate結(jié)構(gòu)中的slots_to_keys跳躍表保存槽和鍵之間的關(guān)系


    image.png
  • 每當節(jié)點往數(shù)據(jù)庫新天一個鍵值時,節(jié)點就會對這個鍵以及鍵的槽號做關(guān)聯(lián)罐柳,比如lst鍵 所在的跳躍表分值為3347 嗲表所關(guān)聯(lián)的slot為3347


    image.png
  • 該跳躍表可以很表的對屬于某個或者某些槽的數(shù)據(jù)庫鍵進行批量操作
重新分片
  • 指將任意數(shù)量的某個節(jié)點的slots分配給其他節(jié)點
  • 重新分片可以在線進行,并且源節(jié)點和目標節(jié)點都可以繼續(xù)處理命令請求
  • redis-trib是負責執(zhí)行分片的具體步驟如下:


    image.png
image.png
ASK錯誤
  • 即當要處理的鍵已經(jīng)被遷移到目標節(jié)點狰住,那么源節(jié)點返回ASK錯誤张吉,指引客戶端找到真正的鍵所在的位置


    image.png
  • 一個槽可能包含多個鍵

cluster setslot importing命令的實現(xiàn)

  • clusterState結(jié)構(gòu)的importing_slots_from數(shù)組記錄了當前節(jié)點正在從其其他節(jié)點導入的槽


    image.png
  • 如果importing_slots_from[i]的值不為null,而是指向一個clusterNode結(jié)構(gòu)催植,那么標識當前節(jié)點正在從clusterNode所代表的節(jié)點導入槽i
  • cluster setslot<i> importing <source_id> 可以將目標節(jié)點的importing_slots_from[i]的值設置為source_id所代表節(jié)點的ClusterNode肮蛹,具體如下圖


    image.png

cluster setslot migranting 命令的實現(xiàn)

  • clusterState結(jié)構(gòu)的migrating_slots_to數(shù)組記錄了當前節(jié)點正在遷移至其他節(jié)點的槽


    image.png
  • 如果migrating_slots_to[i]不為null 而是指向一個clusterNode結(jié)構(gòu)勺择,那么標識當前節(jié)點正在將槽i遷移至clusternode所代表的節(jié)點
  • 命令cluster setSLOT<i> migrating <target_id>,代表將migrating_slots_to[i]設置為其即將遷移的節(jié)點的ClusterNode


    image.png

ASK錯誤
比如下面的例子


image.png
image.png

ASKING命令

  • 該命令唯一要做的就是打開發(fā)送該命令的客戶端的flags蔗崎,即REDIS_ASKING標識
  • 節(jié)點判斷是否指向客戶端命令的過程如下


    image.png
  • 當節(jié)點按照最終的邏輯執(zhí)行槽i命令,但是槽i已經(jīng)遷移了 則返回ASK錯誤扰藕。
  • 當客戶端轉(zhuǎn)向真正的節(jié)點的時候需要先發(fā)送ASKING命令 以此來區(qū)分自己是因為ASK才來這個節(jié)點執(zhí)行命令缓苛,否則會因為計算槽的算法返回MOVED錯誤
  • REDIS_ASKING是一次性標識,當節(jié)點執(zhí)行了有REDIS_ASKING標識的客戶端命令后邓深,客戶端的該標識就會被清除

ASK錯誤和MOVED錯誤的區(qū)別

  • 主要就是前者代表槽正在轉(zhuǎn)移未桥,后者代表槽不屬于自己管轄


    image.png
復制與故障轉(zhuǎn)移
  • redis集群中主節(jié)點用于處理槽,而從節(jié)點則用于復制某個主節(jié)點


    image.png

    image.png
  • 如果7000節(jié)點進入下線芥备,那么集群中仍在正在運作的一個主節(jié)點將在原7000節(jié)點的2個從節(jié)點選擇一個節(jié)點作為新的主節(jié)點


    image.png
  • 當7000重新上線后 只能成為7004的從節(jié)點


    image.png
設置從節(jié)點
  • cluster replicate <node_id> 可以讓接收到命令的節(jié)點成為所指定節(jié)點的從節(jié)點冬耿,并開始進行復制
  • 接受到該命令的節(jié)點會在自己的clustestate.nodes中找到該node_id的clusterNode結(jié)構(gòu),并將自己的clusterState.myself.slaveof指針指向這個結(jié)構(gòu)萌壳,以此來記錄這個節(jié)點正在復制的主節(jié)點:
  • 節(jié)點也會修改在自己的clusterState.myself.flags中的屬性亦镶,關(guān)閉原本的REDIS_NODE_MASTER標識,并打開REDIS_NODE_SLAVE標識
  • 最后節(jié)點會調(diào)用復制方法


    image.png
  • 集群中的所有節(jié)點都會在代表主節(jié)點的clusterNode結(jié)構(gòu)的slaves屬性和numslaves屬性中記錄正在復制這個主節(jié)點的從節(jié)點名單


    image.png
image.png
故障檢測
  • 集群中的每個節(jié)點都會定期的像集群中的其他節(jié)點發(fā)送ping消息袱瓮,如果接受節(jié)點在規(guī)定時間回復PONG則ok缤骨,否則發(fā)送方會標記為接受方為疑似下線
  • 集群中的各個節(jié)點會通過互相發(fā)送消息的方式來交換集群中各個節(jié)點的狀態(tài)信息。
  • 當主節(jié)點A通過消息得知主節(jié)點B認為主節(jié)點C進入疑似下線尺借,那么主節(jié)點A會在自己的clusterState.nodes中找到主節(jié)點C所對應的ClusterNode結(jié)構(gòu)绊起,并將主節(jié)點B的下線報告添加到clusterNode結(jié)構(gòu)的fail_reports


    image.png
image.png
  • 如果一個集群半數(shù)疑似負責處理槽的主節(jié)點都將某個主節(jié)點X報告為疑似下線,那么主節(jié)點x將被標記下線燎斩,然后將消息廣播到其他節(jié)點


    image.png
故障轉(zhuǎn)移
image.png
選舉新的主節(jié)點
image.png

image.png
  • 與選舉sentinel選舉相似 都是基于raft算法選舉的
消息

節(jié)點發(fā)送的消息有五種

  • MEET(GOSSIP)消息:請求接受者加入到發(fā)送者所在的集群

  • PING(GOSSIP)消息:檢測是否在線

  • PONG(GOSSIP)消息:對MEET和PING消息的回復虱歪,也可以廣播向其他節(jié)點,刷新其他節(jié)點對自己的認識(比如故障轉(zhuǎn)移操作執(zhí)行成功之后)

  • FAIL消息:當節(jié)點A判斷節(jié)點B fail之后栅表,A會像集群廣播一條關(guān)于節(jié)點B的Fail消息笋鄙,其他收到消息這會立即將節(jié)點B標記為已下線

  • PUBLISH消息:節(jié)點接收到一條個該命令后節(jié)點會執(zhí)行該命令,并向集群中廣播一條PUBLISH消息


    image.png

    image.png
  • 一條消息由消息頭(header)和正文(data)組成

消息頭

消息頭包含消息正文和記錄了消息發(fā)送者的一些信息
對應著clusterMsg結(jié)構(gòu)


image.png

image.png
  • 其中clusterMsg.data 屬性指向了聯(lián)合ClusterMsgData 也就是消息的正文


    image.png
  • 節(jié)點可以根據(jù)clusterMsg結(jié)構(gòu)的currentEpoch怪瓶,sender局装,myslots等屬性找到發(fā)生者clusterNode結(jié)構(gòu)并進行更新

MEET ,PING,PONG消息的實現(xiàn)

  • redis集群的各個節(jié)點通過gossip協(xié)議來交換各自關(guān)于不同節(jié)點的狀態(tài)信息,其中MEET ,PING,PONG是gossip協(xié)議的三種消息實現(xiàn)劳殖,他們的正文都是clusterMsgDataGossip


    image.png
  • 因為三種消息正文相同铐尚,所以我們通過消息頭的type屬性來判斷一條消息具體是什么消息

  • 每次發(fā)送這三種類型的消息的時候,發(fā)送這都從自己的已知節(jié)點列表隨機選擇兩個節(jié)點(主從都可以)哆姻,并將這兩個被選擇節(jié)點的消息分別保存到兩個clusterMsgDataGossip結(jié)構(gòu)中


    image.png
  • 接受者根據(jù)接受到信息去自己的已知列表中查看是否有被隨機選擇的節(jié)點信息宣增,有就更新,沒有就進行握手


    image.png

FAIL消息的實現(xiàn)

  • 主節(jié)點A將主節(jié)點B標記為已下線的FAIL時矛缨,節(jié)點A會像集群廣播一條關(guān)于主節(jié)點B的Fail消息爹脾,所有收到該消息的節(jié)點都會將節(jié)點B標記為已經(jīng)下線
  • 如果我們使用gossip協(xié)議帖旨,那么可能需要多次通信才能被集群所有節(jié)點知道B下線,而使用Fail只需要一次
  • 對應的數(shù)據(jù)結(jié)構(gòu)是clusterMsgDataFail


    image.png
  • 只包含一個nodename灵妨,其記錄了已下線節(jié)點的名字解阅,因為集群中名字獨一無二

PUBLISH消息的實現(xiàn)

  • 比如客戶端使用publish channel message發(fā)向集群中的某個節(jié)點
  • 接收到publish消息的及誒按不僅會向channel頻道發(fā)送message,還會像集群廣播一條publish消息泌霍,進而導致其他節(jié)點也會向該channel發(fā)送message
  • 數(shù)據(jù)結(jié)構(gòu)是clusterMsgDataPublish


    image.png
image.png
  • bulk_data 是一個字節(jié)數(shù)組货抄,這個字節(jié)數(shù)組保存了客戶端通過publish命令發(fā)給節(jié)點的channel參數(shù)和message參數(shù)
  • 其中bulk_data中的0-channel_len-1保存的是channel參數(shù),channel_len至channel_len+message_len_1字節(jié)則是保存的message參數(shù)
  • 這個channel類似于mq中的channel


    image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末朱转,一起剝皮案震驚了整個濱河市蟹地,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌藤为,老刑警劉巖怪与,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異缅疟,居然都是意外死亡分别,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門存淫,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茎杂,“玉大人,你說我怎么就攤上這事纫雁』屯” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵轧邪,是天一觀的道長刽脖。 經(jīng)常有香客問我,道長忌愚,這世上最難降的妖魔是什么曲管? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮硕糊,結(jié)果婚禮上院水,老公的妹妹穿的比我還像新娘。我一直安慰自己简十,他們只是感情好檬某,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著螟蝙,像睡著了一般恢恼。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胰默,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天场斑,我揣著相機與錄音漓踢,去河邊找鬼。 笑死漏隐,一個胖子當著我的面吹牛喧半,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播青责,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼挺据,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了爽柒?” 一聲冷哼從身側(cè)響起吴菠,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤者填,失蹤者是張志新(化名)和其女友劉穎浩村,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體占哟,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡心墅,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了榨乎。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片怎燥。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蜜暑,靈堂內(nèi)的尸體忽然破棺而出铐姚,到底是詐尸還是另有隱情,我是刑警寧澤肛捍,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布隐绵,位于F島的核電站,受9級特大地震影響拙毫,放射性物質(zhì)發(fā)生泄漏依许。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一缀蹄、第九天 我趴在偏房一處隱蔽的房頂上張望峭跳。 院中可真熱鬧,春花似錦缺前、人聲如沸蛀醉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽滞欠。三九已至,卻和暖如春肆良,著一層夾襖步出監(jiān)牢的瞬間筛璧,已是汗流浹背逸绎。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留夭谤,地道東北人棺牧。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像朗儒,于是被迫代替她去往敵國和親颊乘。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容