Redis集群-復(fù)制與故障轉(zhuǎn)移

????Redis集群中的節(jié)點(diǎn)分為主節(jié)點(diǎn)(master)和從節(jié)點(diǎn)(slave),其中主節(jié)點(diǎn)用于處理槽,而從節(jié)點(diǎn)則用于復(fù)制某個(gè)主節(jié)點(diǎn),并在被復(fù)制的主節(jié)點(diǎn)下線時(shí),代替下線主節(jié)點(diǎn)繼續(xù)處理命令請(qǐng)求。

設(shè)置從節(jié)點(diǎn)

CLUSTER REPLICATE <node_id>
#讓接收命令的節(jié)點(diǎn)成為node_id所指定節(jié)點(diǎn)的從節(jié)點(diǎn),并開(kāi)始對(duì)主節(jié)點(diǎn)進(jìn)行主從復(fù)制。

過(guò)程:

1.接收到該命令的節(jié)點(diǎn)首先會(huì)在自己的clusterState.nodes字典中找到node_id 所對(duì)應(yīng)節(jié)點(diǎn)的clusterNode結(jié)構(gòu),并將自己的clusterState.myself.slaveof 指針指向這個(gè)結(jié)構(gòu),以此來(lái)記錄這個(gè)節(jié)點(diǎn)正在復(fù)制的主節(jié)點(diǎn)∶

struct clusterNode {
   // 如果這是一個(gè)從節(jié)點(diǎn),那么指向主節(jié)點(diǎn)
  struct clusterNode *slaveof;
}

2.然后節(jié)點(diǎn)會(huì)修改自己在clusterState.myself.flags中的屬性,關(guān)閉原本的REDIS_NODE_MASTER標(biāo)識(shí),打開(kāi)REDIS_NODE_SLAVE標(biāo)識(shí),表示這個(gè)節(jié)點(diǎn)已經(jīng)由原來(lái)的主節(jié)點(diǎn)變成了從節(jié)點(diǎn)。

3.最后,節(jié)點(diǎn)會(huì)調(diào)用復(fù)制代碼,并根據(jù)clusterState.myself.slaveof指向的clusterNode結(jié)構(gòu)所保存的IP地址和端口號(hào),對(duì)主節(jié)點(diǎn)進(jìn)行復(fù)制。因?yàn)楣?jié)點(diǎn)的復(fù)制功能和單機(jī) Redis服務(wù)器的復(fù)制功能使用了相同的代碼,所以讓從節(jié)點(diǎn)復(fù)制主節(jié)點(diǎn)相當(dāng)于向從節(jié)點(diǎn)發(fā)送命令SLAVEOF<master_ip><master_port>。

下圖表示從節(jié)點(diǎn)7004正在復(fù)制主節(jié)點(diǎn)7000


image.png

故障檢測(cè)

????集群中的每個(gè)節(jié)點(diǎn)都會(huì)定期向其他節(jié)點(diǎn)發(fā)送PING消息,用來(lái)檢測(cè)對(duì)方是否在線,如果接收PING消息的節(jié)點(diǎn)沒(méi)有在規(guī)定的時(shí)間內(nèi),向發(fā)送PING 消息的節(jié)點(diǎn)返回PONG消息,那么發(fā)送PING消息的節(jié)點(diǎn)就會(huì)將接收PING消息的節(jié)點(diǎn)標(biāo)記為疑似下線

????集群中的各個(gè)節(jié)點(diǎn)會(huì)通過(guò)互相發(fā)送消息的方式來(lái)交換集群中各個(gè)節(jié)點(diǎn)的狀態(tài)信息,例如某個(gè)節(jié)點(diǎn)是處于在線狀態(tài)、疑似下線狀態(tài)(PFAIL),還是已下線狀態(tài)(FAIL)。

????當(dāng)一個(gè)主節(jié)點(diǎn)A通過(guò)消息得知主節(jié)點(diǎn)B認(rèn)為主節(jié)點(diǎn)C進(jìn)入了疑似下線狀態(tài)時(shí),主節(jié)點(diǎn)A會(huì)在自己的clusterState.nodes字典中找到主節(jié)點(diǎn)C所對(duì)應(yīng)的clusterNode結(jié)構(gòu),并將主節(jié)點(diǎn)B的下線報(bào)告(failure report)添加到clusterNode結(jié)構(gòu)的fail_reports鏈表中。

struct clusterNode {

  // 一個(gè)鏈表,記錄了所有其他節(jié)點(diǎn)對(duì)該節(jié)點(diǎn)的下線報(bào)告
  list *fail_reports;
}

????比如,如果主節(jié)點(diǎn)7001在收到主節(jié)點(diǎn)7002、主節(jié)點(diǎn)7003發(fā)送的消息后得知,主節(jié)點(diǎn)7002和主節(jié)點(diǎn)7003都認(rèn)為主節(jié)點(diǎn)7000進(jìn)入了疑似下線狀態(tài),那么主節(jié)點(diǎn)7001將為主節(jié)點(diǎn)7000創(chuàng)建下圖所示的下線報(bào)告。


image.png

????如果在一個(gè)集群里面,半數(shù)以上負(fù)責(zé)處理槽的主節(jié)點(diǎn)都將某個(gè)主節(jié)點(diǎn)x報(bào)告為疑似下線,那么這個(gè)主節(jié)點(diǎn)x將被標(biāo)記為已下線(FAIL),將主節(jié)點(diǎn)x標(biāo)記為已下線的節(jié)點(diǎn)會(huì)向集群廣播一條關(guān)于主節(jié)點(diǎn)x的FAIL消息,所有收到這條 FAIL消息的節(jié)點(diǎn)都會(huì)立即將主節(jié)點(diǎn)x標(biāo)記為已下線。

故障轉(zhuǎn)移

????當(dāng)一個(gè)從節(jié)點(diǎn)發(fā)現(xiàn)自己正在復(fù)制的主節(jié)點(diǎn)進(jìn)入了已下線狀態(tài)時(shí),從節(jié)點(diǎn)將開(kāi)始對(duì)下線主節(jié)點(diǎn)進(jìn)行故障轉(zhuǎn)移,以下是故障轉(zhuǎn)移的執(zhí)行步驟∶
1)復(fù)制下線主節(jié)點(diǎn)的所有從節(jié)點(diǎn)里面,會(huì)有一個(gè)從節(jié)點(diǎn)被選中。
2)被選中的從節(jié)點(diǎn)會(huì)執(zhí)行 SLAVE OF no one命令,成為新的主節(jié)點(diǎn)。
3)新的主節(jié)點(diǎn)會(huì)撤銷(xiāo)所有對(duì)已下線主節(jié)點(diǎn)的槽指派,并將這些槽全部指派給自己。
4)新的主節(jié)點(diǎn)向集群廣播一條PONG消息,這條 PONG消息可以讓集群中的其他節(jié)點(diǎn)立即知道這個(gè)節(jié)點(diǎn)已經(jīng)由從節(jié)點(diǎn)變成了主節(jié)點(diǎn),并且這個(gè)主節(jié)點(diǎn)已經(jīng)接管了原本由已下線節(jié)點(diǎn)負(fù)責(zé)處理的槽。
5)新的主節(jié)點(diǎn)開(kāi)始接收和自己負(fù)責(zé)處理的槽有關(guān)的命令請(qǐng)求,故障轉(zhuǎn)移完成。

選舉新的主節(jié)點(diǎn)

1)集群的配置紀(jì)元是一個(gè)自增計(jì)數(shù)器,它的初始值為0。
2)當(dāng)集群里的某個(gè)節(jié)點(diǎn)開(kāi)始一次故障轉(zhuǎn)移操作時(shí),集群配置紀(jì)元的值會(huì)被增一。
3)對(duì)于每個(gè)配置紀(jì)元,集群里每個(gè)負(fù)責(zé)處理槽的主節(jié)點(diǎn)都有一次投票的機(jī)會(huì),而第一個(gè)向主節(jié)點(diǎn)要求投票的從節(jié)點(diǎn)將獲得主節(jié)點(diǎn)的投票。
4)當(dāng)從節(jié)點(diǎn)發(fā)現(xiàn)自己正在復(fù)制的主節(jié)點(diǎn)進(jìn)入已下線狀態(tài)時(shí),從節(jié)點(diǎn)會(huì)向集群廣播一條CLUSTERMSG_TYPE_FAILOVER_AUTH_REQUEST消息,要求所有收到這條消息、并且具有投票權(quán)的主節(jié)點(diǎn)向這個(gè)從節(jié)點(diǎn)投票。
5)如果一個(gè)主節(jié)點(diǎn)具有投票權(quán)(它正在負(fù)責(zé)處理槽),并且這個(gè)主節(jié)點(diǎn)尚未投票給其他從節(jié)點(diǎn),那么主節(jié)點(diǎn)將向要求投票的從節(jié)點(diǎn)返回一條CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK 消息,表示這個(gè)主節(jié)點(diǎn)支持從節(jié)點(diǎn)成為新的主節(jié)點(diǎn)。
6)每個(gè)參與選舉的從節(jié)點(diǎn)都會(huì)接收CLUSTERMSG_TYPE_FAILOVER_AUTH_ACK消息,并根據(jù)自己收到了多少條這種消息來(lái)統(tǒng)計(jì)自己獲得了多少主節(jié)點(diǎn)的支持。
7)如果集群里有N個(gè)具有投票權(quán)的主節(jié)點(diǎn),那么當(dāng)一個(gè)從節(jié)點(diǎn)收集到大于等于N/2+1 張支持票時(shí),這個(gè)從節(jié)點(diǎn)就會(huì)當(dāng)選為新的主節(jié)點(diǎn)。
8)因?yàn)樵诿恳粋€(gè)配置紀(jì)元里面,每個(gè)具有投票權(quán)的主節(jié)點(diǎn)只能投一次票,所以如果有N個(gè)主節(jié)點(diǎn)進(jìn)行投票,那么具有大于等于N/2+1 張支持票的從節(jié)點(diǎn)只會(huì)有一個(gè),這確保了新的主節(jié)點(diǎn)只會(huì)有一個(gè)。
9)如果在一個(gè)配置紀(jì)元里面沒(méi)有從節(jié)點(diǎn)能收集到足夠多的支持票,那么集群進(jìn)入一個(gè)新的配置紀(jì)元,并再次進(jìn)行選舉,直到選出新的主節(jié)點(diǎn)為止。

集群選取新的主節(jié)點(diǎn)和選舉領(lǐng)頭Sentinel方法非常類似,都是基于Raft算法實(shí)現(xiàn)的。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容