面試官:為什么 Redis 要有哨兵?
網(wǎng)站有讀者留言問(wèn):如何進(jìn)行故障轉(zhuǎn)移?

所以,后面我就補(bǔ)充了這部分內(nèi)容。

話不多說(shuō),發(fā)車(chē)!
為什么要有哨兵機(jī)制?
在 Redis 的主從架構(gòu)中,由于主從模式是讀寫(xiě)分離的,如果主節(jié)點(diǎn)(master)掛了,那么將沒(méi)有主節(jié)點(diǎn)來(lái)服務(wù)客戶端的寫(xiě)操作請(qǐng)求,也沒(méi)有主節(jié)點(diǎn)給從節(jié)點(diǎn)(slave)進(jìn)行數(shù)據(jù)同步了。

主節(jié)點(diǎn)掛了
這時(shí)如果要恢復(fù)服務(wù)的話,需要人工介入,選擇一個(gè)「從節(jié)點(diǎn)」切換為「主節(jié)點(diǎn)」,然后讓其他從節(jié)點(diǎn)指向新的主節(jié)點(diǎn),同時(shí)還需要通知上游那些連接 Redis 主節(jié)點(diǎn)的客戶端,將其配置中的主節(jié)點(diǎn) IP 地址更新為「新主節(jié)點(diǎn)」的 IP 地址。
這樣也不太“智能”了,要是有一個(gè)節(jié)點(diǎn)能監(jiān)控「主節(jié)點(diǎn)」的狀態(tài),當(dāng)發(fā)現(xiàn)主節(jié)點(diǎn)掛了 ,它自動(dòng)將一個(gè)「從節(jié)點(diǎn)」切換為「主節(jié)點(diǎn)」的話,那么可以節(jié)省我們很多事情??!
Redis 在 2.8 版本以后提供的哨兵(Sentinel)機(jī)制,它的作用是實(shí)現(xiàn)主從節(jié)點(diǎn)故障轉(zhuǎn)移。它會(huì)監(jiān)測(cè)主節(jié)點(diǎn)是否存活,如果發(fā)現(xiàn)主節(jié)點(diǎn)掛了,它就會(huì)選舉一個(gè)從節(jié)點(diǎn)切換為主節(jié)點(diǎn),并且把新主節(jié)點(diǎn)的相關(guān)信息通知給從節(jié)點(diǎn)和客戶端。
哨兵機(jī)制是如何工作的?
哨兵其實(shí)是一個(gè)運(yùn)行在特殊模式下的 Redis 進(jìn)程,所以它也是一個(gè)節(jié)點(diǎn)。從“哨兵”這個(gè)名字也可以看得出來(lái),它相當(dāng)于是“觀察者節(jié)點(diǎn)”,觀察的對(duì)象是主從節(jié)點(diǎn)。
當(dāng)然,它不僅僅是觀察那么簡(jiǎn)單,在它觀察到有異常的狀況下,會(huì)做出一些“動(dòng)作”,來(lái)修復(fù)異常狀態(tài)。
哨兵節(jié)點(diǎn)主要負(fù)責(zé)三件事情:監(jiān)控、選主、通知。

哨兵的職責(zé)
所以,我們重點(diǎn)要學(xué)習(xí)這三件事情:
- 哨兵節(jié)點(diǎn)是如何監(jiān)控節(jié)點(diǎn)的?又是如何判斷主節(jié)點(diǎn)是否真的故障了?
- 根據(jù)什么規(guī)則選擇一個(gè)從節(jié)點(diǎn)切換為主節(jié)點(diǎn)?
- 怎么把新主節(jié)點(diǎn)的相關(guān)信息通知給從節(jié)點(diǎn)和客戶端呢?
如何判斷主節(jié)點(diǎn)真的故障了?
哨兵會(huì)每隔 1 秒給所有主從節(jié)點(diǎn)發(fā)送 PING 命令,當(dāng)主從節(jié)點(diǎn)收到 PING 命令后,會(huì)發(fā)送一個(gè)響應(yīng)命令給哨兵,這樣就可以判斷它們是否在正常運(yùn)行。

哨兵監(jiān)控主從節(jié)點(diǎn)
如果主節(jié)點(diǎn)或者從節(jié)點(diǎn)沒(méi)有在規(guī)定的時(shí)間內(nèi)響應(yīng)哨兵的 PING 命令,哨兵就會(huì)將它們標(biāo)記為「主觀下線」。這個(gè)「規(guī)定的時(shí)間」是配置項(xiàng) down-after-milliseconds 參數(shù)設(shè)定的,單位是毫秒。
主觀下線?難道還有客觀下線?
是的沒(méi)錯(cuò),客觀下線只適用于主節(jié)點(diǎn)。
之所以針對(duì)「主節(jié)點(diǎn)」設(shè)計(jì)「主觀下線」和「客觀下線」兩個(gè)狀態(tài),是因?yàn)橛锌赡堋钢鞴?jié)點(diǎn)」其實(shí)并沒(méi)有故障,可能只是因?yàn)橹鞴?jié)點(diǎn)的系統(tǒng)壓力比較大或者網(wǎng)絡(luò)發(fā)送了擁塞,導(dǎo)致主節(jié)點(diǎn)沒(méi)有在規(guī)定時(shí)間內(nèi)響應(yīng)哨兵的 PING 命令。
所以,為了減少誤判的情況,哨兵在部署的時(shí)候不會(huì)只部署一個(gè)節(jié)點(diǎn),而是用多個(gè)節(jié)點(diǎn)部署成哨兵集群(最少需要三臺(tái)機(jī)器來(lái)部署哨兵集群),通過(guò)多個(gè)哨兵節(jié)點(diǎn)一起判斷,就可以就可以避免單個(gè)哨兵因?yàn)樽陨砭W(wǎng)絡(luò)狀況不好,而誤判主節(jié)點(diǎn)下線的情況。同時(shí),多個(gè)哨兵的網(wǎng)絡(luò)同時(shí)不穩(wěn)定的概率較小,由它們一起做決策,誤判率也能降低。
具體是怎么判定主節(jié)點(diǎn)為「客觀下線」的呢?
當(dāng)一個(gè)哨兵判斷主節(jié)點(diǎn)為「主觀下線」后,就會(huì)向其他哨兵發(fā)起命令,其他哨兵收到這個(gè)命令后,就會(huì)根據(jù)自身和主節(jié)點(diǎn)的網(wǎng)絡(luò)狀況,做出贊成投票或者拒絕投票的響應(yīng)。

當(dāng)這個(gè)哨兵的贊同票數(shù)達(dá)到哨兵配置文件中的 quorum 配置項(xiàng)設(shè)定的值后,這時(shí)主節(jié)點(diǎn)就會(huì)被該哨兵標(biāo)記為「客觀下線」。
例如,現(xiàn)在有 3 個(gè)哨兵,quorum 配置的是 2,那么一個(gè)哨兵需要 2 張贊成票,就可以標(biāo)記主節(jié)點(diǎn)為“客觀下線”了。這 2 張贊成票包括哨兵自己的一張贊成票和另外兩個(gè)哨兵的贊成票。
PS:quorum 的值一般設(shè)置為哨兵個(gè)數(shù)的二分之一加1,例如 3 個(gè)哨兵就設(shè)置 2。
哨兵判斷完主節(jié)點(diǎn)客觀下線后,哨兵就要開(kāi)始在多個(gè)「從節(jié)點(diǎn)」中,選出一個(gè)從節(jié)點(diǎn)來(lái)做新主節(jié)點(diǎn)。
由哪個(gè)哨兵進(jìn)行主從故障轉(zhuǎn)移?
前面說(shuō)過(guò),為了更加“客觀”的判斷主節(jié)點(diǎn)故障了,一般不會(huì)只由單個(gè)哨兵的檢測(cè)結(jié)果來(lái)判斷,而是多個(gè)哨兵一起判斷,這樣可以減少誤判概率,所以哨兵是以哨兵集群的方式存在的。
問(wèn)題來(lái)了,由哨兵集群中的哪個(gè)節(jié)點(diǎn)進(jìn)行主從故障轉(zhuǎn)移呢?
所以這時(shí)候,還需要在哨兵集群中選出一個(gè) leeder,讓 leeder 來(lái)執(zhí)行主從切換。
選舉 leeder 的過(guò)程其實(shí)是一個(gè)投票的過(guò)程,在投票開(kāi)始前,肯定得有個(gè)「候選者」。
那誰(shuí)來(lái)作為候選者呢?
哪個(gè)哨兵節(jié)點(diǎn)判斷主節(jié)點(diǎn)為「客觀下線」,這個(gè)哨兵節(jié)點(diǎn)就是候選者,所謂的候選者就是想當(dāng) Leader 的哨兵。
舉個(gè)例子,假設(shè)有三個(gè)哨兵。當(dāng)哨兵 B 先判斷到主節(jié)點(diǎn)「主觀下線后」,就會(huì)給其他實(shí)例發(fā)送 is-master-down-by-addr 命令。接著,其他哨兵會(huì)根據(jù)自己和主節(jié)點(diǎn)的網(wǎng)絡(luò)連接情況,做出贊成投票或者拒絕投票的響應(yīng)。

當(dāng)哨兵 B 收到贊成票數(shù)達(dá)到哨兵配置文件中的 quorum 配置項(xiàng)設(shè)定的值后,就會(huì)將主節(jié)點(diǎn)標(biāo)記為「客觀下線」,此時(shí)的哨兵 B 就是一個(gè)Leader 候選者。
候選者如何選舉成為 Leader?
候選者會(huì)向其他哨兵發(fā)送命令,表明希望成為 Leader 來(lái)執(zhí)行主從切換,并讓所有其他哨兵對(duì)它進(jìn)行投票。
每個(gè)哨兵只有一次投票機(jī)會(huì),如果用完后就不能參與投票了,可以投給自己或投給別人,但是只有候選者才能把票投給自己。
那么在投票過(guò)程中,任何一個(gè)「候選者」,要滿足兩個(gè)條件:
- 第一,拿到半數(shù)以上的贊成票;
- 第二,拿到的票數(shù)同時(shí)還需要大于等于哨兵配置文件中的 quorum 值。
舉個(gè)例子,假設(shè)哨兵節(jié)點(diǎn)有 3 個(gè),quorum 設(shè)置為 2,那么任何一個(gè)想成為 Leader 的哨兵只要拿到 2 張贊成票,就可以選舉成功了。如果沒(méi)有滿足條件,就需要重新進(jìn)行選舉。
這時(shí)候有的同學(xué)就會(huì)問(wèn)了,如果某個(gè)時(shí)間點(diǎn),剛好有兩個(gè)哨兵節(jié)點(diǎn)判斷到主節(jié)點(diǎn)為客觀下線,那這時(shí)不就有兩個(gè)候選者了?這時(shí)該如何決定誰(shuí)是 Leader 呢?
每位候選者都會(huì)先給自己投一票,然后向其他哨兵發(fā)起投票請(qǐng)求。如果投票者先收到「候選者 A」的投票請(qǐng)求,就會(huì)先投票給它,如果投票者用完投票機(jī)會(huì)后,收到「候選者 B」的投票請(qǐng)求后,就會(huì)拒絕投票。這時(shí),候選者 A 先滿足了上面的那兩個(gè)條件,所以「候選者 A」就會(huì)被選舉為 Leader。
為什么哨兵節(jié)點(diǎn)至少要有 3 個(gè)?
如果哨兵集群中只有 2 個(gè)哨兵節(jié)點(diǎn),此時(shí)如果一個(gè)哨兵想要成功成為 Leader,必須獲得 2 票,而不是 1 票。
所以,如果哨兵集群中有個(gè)哨兵掛掉了,那么就只剩一個(gè)哨兵了,如果這個(gè)哨兵想要成為 Leader,這時(shí)票數(shù)就沒(méi)辦法達(dá)到 2 票,就無(wú)法成功成為 Leader,這時(shí)是無(wú)法進(jìn)行主從節(jié)點(diǎn)切換的。
因此,通常我們至少會(huì)配置 3 個(gè)哨兵節(jié)點(diǎn)。這時(shí),如果哨兵集群中有個(gè)哨兵掛掉了,那么還剩下兩個(gè)個(gè)哨兵,如果這個(gè)哨兵想要成為 Leader,這時(shí)還是有機(jī)會(huì)達(dá)到 2 票的,所以還是可以選舉成功的,不會(huì)導(dǎo)致無(wú)法進(jìn)行主從節(jié)點(diǎn)切換。
當(dāng)然,你要問(wèn),如果 3 個(gè)哨兵節(jié)點(diǎn),掛了 2 個(gè)怎么辦?這個(gè)時(shí)候得人為介入了,或者增加多一點(diǎn)哨兵節(jié)點(diǎn)。
再說(shuō)一個(gè)問(wèn)題,Redis 1 主 4 從,5 個(gè)哨兵 ,quorum 設(shè)置為 3,如果 2 個(gè)哨兵故障,當(dāng)主節(jié)點(diǎn)宕機(jī)時(shí),哨兵能否判斷主節(jié)點(diǎn)“客觀下線”?主從能否自動(dòng)切換?
- 哨兵集群可以判定主節(jié)點(diǎn)“客觀下線”。哨兵集群還剩下 3 個(gè)哨兵,當(dāng)一個(gè)哨兵判斷主節(jié)點(diǎn)“主觀下線”后,詢(xún)問(wèn)另外 2 個(gè)哨兵后,有可能能拿到 3 張贊同票,這時(shí)就達(dá)到了 quorum 的值,因此,哨兵集群可以判定主節(jié)點(diǎn)為“客觀下線”。
- 哨兵集群可以完成主從切換。當(dāng)有個(gè)哨兵標(biāo)記主節(jié)點(diǎn)為「客觀下線」后,就會(huì)進(jìn)行選舉 Leader 的過(guò)程,因?yàn)榇藭r(shí)哨兵集群還剩下 3 個(gè)哨兵,那么還是可以拿到半數(shù)以上(5/2+1=3)的票,而且也達(dá)到了 quorum 值,滿足了選舉 Leader 的兩個(gè)條件, 所以就能選舉成功,因此哨兵集群可以完成主從切換。
如果 quorum 設(shè)置為 2 ,并且如果有 3 個(gè)哨兵故障的話。此時(shí)哨兵集群還是可以判定主節(jié)點(diǎn)為“客觀下線”,但是哨兵不能完成主從切換了,大家可以自己推演下。
如果 quorum 設(shè)置為 3,并且如果有 3 個(gè)哨兵故障的話,哨兵集群即不能判定主節(jié)點(diǎn)為“客觀下線”,也不能完成主從切換了。
可以看到,quorum 為 2 的時(shí)候,并且如果有 3 個(gè)哨兵故障的話,雖然可以判定主節(jié)點(diǎn)為“客觀下線”,但是不能完成主從切換,這樣感覺(jué)「判定主節(jié)點(diǎn)為客觀下線」這件事情白做了一樣,既然這樣,還不如不要做,quorum 為 3 的時(shí)候,就可以避免這種無(wú)用功。
所以,quorum 的值建議設(shè)置為哨兵個(gè)數(shù)的二分之一加1,例如 3 個(gè)哨兵就設(shè)置 2,5 個(gè)哨兵設(shè)置為 3,而且哨兵節(jié)點(diǎn)的數(shù)量應(yīng)該是奇數(shù)。
主從故障轉(zhuǎn)移的過(guò)程是怎樣的?
在哨兵集群中通過(guò)投票的方式,選舉出了哨兵 leader 后,就可以進(jìn)行主從故障轉(zhuǎn)移的過(guò)程了,如下圖:

主從故障轉(zhuǎn)移操作包含以下四個(gè)步驟:
- 第一步:在已下線主節(jié)點(diǎn)(舊主節(jié)點(diǎn))屬下的所有「從節(jié)點(diǎn)」里面,挑選出一個(gè)從節(jié)點(diǎn),并將其轉(zhuǎn)換為主節(jié)點(diǎn)。
- 第二步:讓已下線主節(jié)點(diǎn)屬下的所有「從節(jié)點(diǎn)」修改復(fù)制目標(biāo),修改為復(fù)制「新主節(jié)點(diǎn)」;
- 第三步:將新主節(jié)點(diǎn)的 IP 地址和信息,通過(guò)「發(fā)布者/訂閱者機(jī)制」通知給客戶端;
- 第四步:繼續(xù)監(jiān)視舊主節(jié)點(diǎn),當(dāng)這個(gè)舊主節(jié)點(diǎn)重新上線時(shí),將它設(shè)置為新主節(jié)點(diǎn)的從節(jié)點(diǎn);
步驟一:選出新主節(jié)點(diǎn)
故障轉(zhuǎn)移操作第一步要做的就是在已下線主節(jié)點(diǎn)屬下的所有「從節(jié)點(diǎn)」中,挑選出一個(gè)狀態(tài)良好、數(shù)據(jù)完整的從節(jié)點(diǎn),然后向這個(gè)「從節(jié)點(diǎn)」發(fā)送 SLAVEOF no one 命令,將這個(gè)「從節(jié)點(diǎn)」轉(zhuǎn)換為「主節(jié)點(diǎn)」。
那么多「從節(jié)點(diǎn)」,到底選擇哪個(gè)從節(jié)點(diǎn)作為新主節(jié)點(diǎn)的?
隨機(jī)的方式好嗎?隨機(jī)的方式,實(shí)現(xiàn)起來(lái)很簡(jiǎn)單,但是如果選到一個(gè)網(wǎng)絡(luò)狀態(tài)不好的從節(jié)點(diǎn)作為新主節(jié)點(diǎn),那么可能在將來(lái)不久又要做一次主從故障遷移。
所以,我們首先要把網(wǎng)絡(luò)狀態(tài)不好的從節(jié)點(diǎn)給過(guò)濾掉。首先把已經(jīng)下線的從節(jié)點(diǎn)過(guò)濾掉,然后把以往網(wǎng)絡(luò)連接狀態(tài)不好的從節(jié)點(diǎn)也給過(guò)濾掉。
怎么判斷從節(jié)點(diǎn)之前的網(wǎng)絡(luò)連接狀態(tài)不好呢?
Redis 有個(gè)叫 down-after-milliseconds * 10 配置項(xiàng),其down-after-milliseconds 是主從節(jié)點(diǎn)斷連的最大連接超時(shí)時(shí)間。如果在 down-after-milliseconds 毫秒內(nèi),主從節(jié)點(diǎn)都沒(méi)有通過(guò)網(wǎng)絡(luò)聯(lián)系上,我們就可以認(rèn)為主從節(jié)點(diǎn)斷連了。如果發(fā)生斷連的次數(shù)超過(guò)了 10 次,就說(shuō)明這個(gè)從節(jié)點(diǎn)的網(wǎng)絡(luò)狀況不好,不適合作為新主節(jié)點(diǎn)。
至此,我們就把網(wǎng)絡(luò)狀態(tài)不好的從節(jié)點(diǎn)過(guò)濾掉了,接下來(lái)要對(duì)所有從節(jié)點(diǎn)進(jìn)行三輪考察:優(yōu)先級(jí)、復(fù)制進(jìn)度、ID 號(hào)。在進(jìn)行每一輪考察的時(shí)候,哪個(gè)從節(jié)點(diǎn)優(yōu)先勝出,就選擇其作為新主節(jié)點(diǎn)。
- 第一輪考察:哨兵首先會(huì)根據(jù)從節(jié)點(diǎn)的優(yōu)先級(jí)來(lái)進(jìn)行排序,優(yōu)先級(jí)越小排名越靠前,
- 第二輪考察:如果優(yōu)先級(jí)相同,則查看復(fù)制的下標(biāo),哪個(gè)從「主節(jié)點(diǎn)」接收的復(fù)制數(shù)據(jù)多,哪個(gè)就靠前。
- 第三輪考察:如果優(yōu)先級(jí)和下標(biāo)都相同,就選擇從節(jié)點(diǎn) ID 較小的那個(gè)。
第一輪考察:優(yōu)先級(jí)最高的從節(jié)點(diǎn)勝出
Redis 有個(gè)叫 slave-priority 配置項(xiàng),可以給從節(jié)點(diǎn)設(shè)置優(yōu)先級(jí)。
每一臺(tái)從節(jié)點(diǎn)的服務(wù)器配置不一定是相同的,我們可以根據(jù)服務(wù)器性能配置來(lái)設(shè)置從節(jié)點(diǎn)的優(yōu)先級(jí)。
比如,如果 「 A 從節(jié)點(diǎn)」的物理內(nèi)存是所有從節(jié)點(diǎn)中最大的, 那么我們可以把「 A 從節(jié)點(diǎn)」的優(yōu)先級(jí)設(shè)置成最高。這樣當(dāng)哨兵進(jìn)行第一輪考慮的時(shí)候,優(yōu)先級(jí)最高的 A 從節(jié)點(diǎn)就會(huì)優(yōu)先勝出,于是就會(huì)成為新主節(jié)點(diǎn)。
第二輪考察:復(fù)制進(jìn)度最靠前的從節(jié)點(diǎn)勝出
如果在第一輪考察中,發(fā)現(xiàn)優(yōu)先級(jí)最高的從節(jié)點(diǎn)有兩個(gè),那么就會(huì)進(jìn)行第二輪考察,比較兩個(gè)從節(jié)點(diǎn)哪個(gè)復(fù)制進(jìn)度。
什么是復(fù)制進(jìn)度?主從架構(gòu)中,主節(jié)點(diǎn)會(huì)將寫(xiě)操作同步給從節(jié)點(diǎn),在這個(gè)過(guò)程中,主節(jié)點(diǎn)會(huì)用 master_repl_offset 記錄當(dāng)前的最新寫(xiě)操作在 repl_backlog_buffer 中的位置(如下圖中的「主服務(wù)器已經(jīng)寫(xiě)入的數(shù)據(jù)」的位置),而從節(jié)點(diǎn)會(huì)用 slave_repl_offset 這個(gè)值記錄當(dāng)前的復(fù)制進(jìn)度(如下圖中的「從服務(wù)器要讀的位置」的位置)。

如果某個(gè)從節(jié)點(diǎn)的 slave_repl_offset 最接近 master_repl_offset,說(shuō)明它的復(fù)制進(jìn)度是最靠前的,于是就可以將它選為新主節(jié)點(diǎn)。
第三輪考察:ID 號(hào)小的從節(jié)點(diǎn)勝出
如果在第二輪考察中,發(fā)現(xiàn)有兩個(gè)從節(jié)點(diǎn)優(yōu)先級(jí)和復(fù)制進(jìn)度都是一樣的,那么就會(huì)進(jìn)行第三輪考察,比較兩個(gè)從節(jié)點(diǎn)的 ID 號(hào),ID 號(hào)小的從節(jié)點(diǎn)勝出。
什么是 ID 號(hào)?每個(gè)從節(jié)點(diǎn)都有一個(gè)編號(hào),這個(gè)編號(hào)就是 ID 號(hào),是用來(lái)唯一標(biāo)識(shí)從節(jié)點(diǎn)的。
到這里,選主的事情終于結(jié)束了。簡(jiǎn)單給大家總結(jié)下:

在選舉出從節(jié)點(diǎn)后,哨兵 leader 向被選中的從節(jié)點(diǎn)發(fā)送 SLAVEOF no one 命令,讓這個(gè)從節(jié)點(diǎn)解除從節(jié)點(diǎn)的身份,將其變?yōu)樾轮鞴?jié)點(diǎn)。
如下圖,哨兵 leader 向被選中的從節(jié)點(diǎn) server2 發(fā)送 SLAVEOF no one 命令,將該從節(jié)點(diǎn)升級(jí)為新主節(jié)點(diǎn)。

在發(fā)送 SLAVEOF no one 命令之后,哨兵 leader 會(huì)以每秒一次的頻率向被升級(jí)的從節(jié)點(diǎn)發(fā)送 INFO 命令(沒(méi)進(jìn)行故障轉(zhuǎn)移之前,INFO 命令的頻率是每十秒一次),并觀察命令回復(fù)中的角色信息,當(dāng)被升級(jí)節(jié)點(diǎn)的角色信息從原來(lái)的 slave 變?yōu)?master 時(shí),哨兵 leader 就知道被選中的從節(jié)點(diǎn)已經(jīng)順利升級(jí)為主節(jié)點(diǎn)了。
如下圖,選中的從節(jié)點(diǎn) server2 升級(jí)成了新主節(jié)點(diǎn):

步驟二:將從節(jié)點(diǎn)指向新主節(jié)點(diǎn)
當(dāng)新主節(jié)點(diǎn)出現(xiàn)之后,哨兵 leader 下一步要做的就是,讓已下線主節(jié)點(diǎn)屬下的所有「從節(jié)點(diǎn)」指向「新主節(jié)點(diǎn)」,這一動(dòng)作可以通過(guò)向「從節(jié)點(diǎn)」發(fā)送 SLAVEOF 命令來(lái)實(shí)現(xiàn)。
如下圖,哨兵 leader 向所有從節(jié)點(diǎn)(server3和server4)發(fā)送 SLAVEOF ,讓它們成為新主節(jié)點(diǎn)的從節(jié)點(diǎn)。

所有從節(jié)點(diǎn)指向新主節(jié)點(diǎn)后的拓?fù)鋱D如下:

步驟三:通知客戶的主節(jié)點(diǎn)已更換
經(jīng)過(guò)前面一系列的操作后,哨兵集群終于完成主從切換的工作,那么新主節(jié)點(diǎn)的信息要如何通知給客戶端呢?
這主要通過(guò) Redis 的發(fā)布者/訂閱者機(jī)制來(lái)實(shí)現(xiàn)的。每個(gè)哨兵節(jié)點(diǎn)提供發(fā)布者/訂閱者機(jī)制,客戶端可以從哨兵訂閱消息。
哨兵提供的消息訂閱頻道有很多,不同頻道包含了主從節(jié)點(diǎn)切換過(guò)程中的不同關(guān)鍵事件,幾個(gè)常見(jiàn)的事件如下:

客戶端和哨兵建立連接后,客戶端會(huì)訂閱哨兵提供的頻道。主從切換完成后,哨兵就會(huì)向 +switch-master 頻道發(fā)布新主節(jié)點(diǎn)的 IP 地址和端口的消息,這個(gè)時(shí)候客戶端就可以收到這條信息,然后用這里面的新主節(jié)點(diǎn)的 IP 地址和端口進(jìn)行通信了。
通過(guò)發(fā)布者/訂閱者機(jī)制機(jī)制,有了這些事件通知,客戶端不僅可以在主從切換后得到新主節(jié)點(diǎn)的連接信息,還可以監(jiān)控到主從節(jié)點(diǎn)切換過(guò)程中發(fā)生的各個(gè)重要事件。這樣,客戶端就可以知道主從切換進(jìn)行到哪一步了,有助于了解切換進(jìn)度。
步驟四:將舊主節(jié)點(diǎn)變?yōu)閺墓?jié)點(diǎn)
故障轉(zhuǎn)移操作最后要做的是,繼續(xù)監(jiān)視舊主節(jié)點(diǎn),當(dāng)舊主節(jié)點(diǎn)重新上線時(shí),哨兵集群就會(huì)向它發(fā)送 SLAVEOF 命令,讓它成為新主節(jié)點(diǎn)的從節(jié)點(diǎn),如下圖:

至此,整個(gè)主從節(jié)點(diǎn)的故障轉(zhuǎn)移的工作結(jié)束。
哨兵集群是如何組成的?
前面提到了 Redis 的發(fā)布者/訂閱者機(jī)制,那就不得不提一下哨兵集群的組成方式,因?yàn)樗灿玫搅诉@個(gè)技術(shù)。
在我第一次搭建哨兵集群的時(shí)候,當(dāng)時(shí)覺(jué)得很詫異。因?yàn)樵谂渲蒙诒男畔r(shí),竟然只需要填下面這幾個(gè)參數(shù),設(shè)置主節(jié)點(diǎn)名字、主節(jié)點(diǎn)的 IP 地址和端口號(hào)以及 quorum 值。
sentinel monitor <master-name> <ip> <redis-port> <quorum>
不需要填其他哨兵節(jié)點(diǎn)的信息,我就好奇它們是如何感知對(duì)方的,又是如何組成哨兵集群的?
后面才了解到,哨兵節(jié)點(diǎn)之間是通過(guò) Redis 的發(fā)布者/訂閱者機(jī)制來(lái)相互發(fā)現(xiàn)的。
在主從集群中,主節(jié)點(diǎn)上有一個(gè)名為_(kāi)_sentinel__:hello的頻道,不同哨兵就是通過(guò)它來(lái)相互發(fā)現(xiàn),實(shí)現(xiàn)互相通信的。
在下圖中,哨兵 A 把自己的 IP 地址和端口的信息發(fā)布到__sentinel__:hello 頻道上,哨兵 B 和 C 訂閱了該頻道。那么此時(shí),哨兵 B 和 C 就可以從這個(gè)頻道直接獲取哨兵 A 的 IP 地址和端口號(hào)。然后,哨兵 B、C 可以和哨兵 A 建立網(wǎng)絡(luò)連接。

通過(guò)這個(gè)方式,哨兵 B 和 C 也可以建立網(wǎng)絡(luò)連接,這樣一來(lái),哨兵集群就形成了。
哨兵集群會(huì)對(duì)「從節(jié)點(diǎn)」的運(yùn)行狀態(tài)進(jìn)行監(jiān)控,那哨兵集群如何知道「從節(jié)點(diǎn)」的信息?
主節(jié)點(diǎn)知道所有「從節(jié)點(diǎn)」的信息,所以哨兵會(huì)每 10 秒一次的頻率向主節(jié)點(diǎn)發(fā)送 INFO 命令來(lái)獲取所有「從節(jié)點(diǎn)」的信息。
如下圖所示,哨兵 B 給主節(jié)點(diǎn)發(fā)送 INFO 命令,主節(jié)點(diǎn)接受到這個(gè)命令后,就會(huì)把從節(jié)點(diǎn)列表返回給哨兵。接著,哨兵就可以根據(jù)從節(jié)點(diǎn)列表中的連接信息,和每個(gè)從節(jié)點(diǎn)建立連接,并在這個(gè)連接上持續(xù)地對(duì)從節(jié)點(diǎn)進(jìn)行監(jiān)控。哨兵 A 和 C 可以通過(guò)相同的方法和從節(jié)點(diǎn)建立連接。

正式通過(guò) Redis 的發(fā)布者/訂閱者機(jī)制,哨兵之間可以相互感知,然后組成集群,同時(shí),哨兵又通過(guò) INFO 命令,在主節(jié)點(diǎn)里獲得了所有從節(jié)點(diǎn)連接信息,于是就能和從節(jié)點(diǎn)建立連接,并進(jìn)行監(jiān)控了。
參考資料:
《Redis 核心技術(shù)與實(shí)戰(zhàn)》
《Redis 設(shè)計(jì)與實(shí)現(xiàn)》
總結(jié)
Redis 在 2.8 版本以后提供的哨兵(Sentinel)機(jī)制,它的作用是實(shí)現(xiàn)主從節(jié)點(diǎn)故障轉(zhuǎn)移。它會(huì)監(jiān)測(cè)主節(jié)點(diǎn)是否存活,如果發(fā)現(xiàn)主節(jié)點(diǎn)掛了,它就會(huì)選舉一個(gè)從節(jié)點(diǎn)切換為主節(jié)點(diǎn),并且把新主節(jié)點(diǎn)的相關(guān)信息通知給從節(jié)點(diǎn)和客戶端。
哨兵一般是以集群的方式部署,至少需要 3 個(gè)哨兵節(jié)點(diǎn),哨兵集群主要負(fù)責(zé)三件事情:監(jiān)控、選主、通知。
哨兵節(jié)點(diǎn)通過(guò) Redis 的發(fā)布者/訂閱者機(jī)制,哨兵之間可以相互感知,相互連接,然后組成哨兵集群,同時(shí)哨兵又通過(guò) INFO 命令,在主節(jié)點(diǎn)里獲得了所有從節(jié)點(diǎn)連接信息,于是就能和從節(jié)點(diǎn)建立連接,并進(jìn)行監(jiān)控了。
1.第一輪投票:判斷主節(jié)點(diǎn)下線
當(dāng)哨兵集群中的某個(gè)哨兵判定主節(jié)點(diǎn)下線(主觀下線)后,就會(huì)向其他哨兵發(fā)起命令,其他哨兵收到這個(gè)命令后,就會(huì)根據(jù)自身和主節(jié)點(diǎn)的網(wǎng)絡(luò)狀況,做出贊成投票或者拒絕投票的響應(yīng)。
當(dāng)這個(gè)哨兵的贊同票數(shù)達(dá)到哨兵配置文件中的 quorum 配置項(xiàng)設(shè)定的值后,這時(shí)主節(jié)點(diǎn)就會(huì)被該哨兵標(biāo)記為「客觀下線」。
2.第二輪投票:選出哨兵leader
某個(gè)哨兵判定主節(jié)點(diǎn)客觀下線后,該哨兵就會(huì)發(fā)起投票,告訴其他哨兵,它想成為 leader,想成為 leader 的哨兵節(jié)點(diǎn),要滿足兩個(gè)條件:
第一,拿到半數(shù)以上的贊成票;
第二,拿到的票數(shù)同時(shí)還需要大于等于哨兵配置文件中的 quorum 值。
3.由哨兵 leader 進(jìn)行主從故障轉(zhuǎn)移
選舉出了哨兵 leader 后,就可以進(jìn)行主從故障轉(zhuǎn)移的過(guò)程了。該操作包含以下四個(gè)步驟:
第一步:在已下線主節(jié)點(diǎn)(舊主節(jié)點(diǎn))屬下的所有「從節(jié)點(diǎn)」里面,挑選出一個(gè)從節(jié)點(diǎn),并將其轉(zhuǎn)換為主節(jié)點(diǎn),選擇的規(guī)則:
過(guò)濾掉已經(jīng)離線的從節(jié)點(diǎn);
過(guò)濾掉歷史網(wǎng)絡(luò)連接狀態(tài)不好的從節(jié)點(diǎn);
將剩下的從節(jié)點(diǎn),進(jìn)行三輪考察:優(yōu)先級(jí)、復(fù)制進(jìn)度、ID 號(hào)。在每一輪考察過(guò)程中,如果找到了一個(gè)勝出的從節(jié)點(diǎn),就將其作為新主節(jié)點(diǎn)。
第二步:讓已下線主節(jié)點(diǎn)屬下的所有「從節(jié)點(diǎn)」修改復(fù)制目標(biāo),修改為復(fù)制「新主節(jié)點(diǎn)」;
第三步:將新主節(jié)點(diǎn)的 IP 地址和信息,通過(guò)「發(fā)布者/訂閱者機(jī)制」通知給客戶端;
第四步:繼續(xù)監(jiān)視舊主節(jié)點(diǎn),當(dāng)這個(gè)舊主節(jié)點(diǎn)重新上線時(shí),將它設(shè)置為新主節(jié)點(diǎn)的從節(jié)點(diǎn);
































