自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

不懂Redis Cluster原理,我被同事diss了!

原創(chuàng)
存儲 存儲軟件 Redis
Redis 緩存作為使用最多的緩存工具被各大廠商爭相使用。通常我們會使用單體的 Redis 應(yīng)用作為緩存服務(wù),為了保證其高可用還會使用主從模式(Master-Slave),又或者是讀寫分離的設(shè)計(jì)。

【51CTO.com原創(chuàng)稿件】Redis 緩存作為使用最多的緩存工具被各大廠商爭相使用。通常我們會使用單體的 Redis 應(yīng)用作為緩存服務(wù),為了保證其高可用還會使用主從模式(Master-Slave),又或者是讀寫分離的設(shè)計(jì)。

[[321092]]

圖片來自 Pexels

但是當(dāng)緩存數(shù)據(jù)量增加以后,無法用單體服務(wù)器承載緩存服務(wù)時,就需要對緩存服務(wù)進(jìn)行擴(kuò)展。

將需要緩存的數(shù)據(jù)切分成不同的分區(qū),將數(shù)據(jù)分區(qū)放到不同的服務(wù)器中,用分布式的緩存來承載高并發(fā)的緩存訪問。恰好 Redis Cluster 方案剛好支持這部分功能。

今天就來一起看看 Redis Cluster 的核心原理和實(shí)踐:

  • Redis Cluster 實(shí)現(xiàn)數(shù)據(jù)分區(qū)
  • 分布式緩存節(jié)點(diǎn)之間的通訊
  • 請求分布式緩存的路由
  • 緩存節(jié)點(diǎn)的擴(kuò)展和收縮
  • 故障發(fā)現(xiàn)和恢復(fù)

Redis Cluster 實(shí)現(xiàn)數(shù)據(jù)分區(qū)

正如開篇中提到的,分布式數(shù)據(jù)庫要解決的就是將整塊數(shù)據(jù),按照規(guī)則分配到多個緩存節(jié)點(diǎn),解決的是單個緩存節(jié)點(diǎn)處理數(shù)量大的問題。

如果要將這些數(shù)據(jù)進(jìn)行拆分,并且存放必須有一個算法。例如:哈希算法和哈希一致性算法,這些比較經(jīng)典的算法。

Redis Cluster 則采用的是虛擬槽分區(qū)算法。其中提到了槽(Slot)的概念。這個槽是用來存放緩存信息的單位,在 Redis 中將存儲空間分成了 16384 個槽,也就是說 Redis Cluster 槽的范圍是 0 -16383(2^4 * 2^10)。

緩存信息通常是用 Key-Value 的方式來存放的,在存儲信息的時候,集群會對 Key 進(jìn)行 CRC16 校驗(yàn)并對 16384 取模(slot = CRC16(key)%16383)。

得到的結(jié)果就是 Key-Value 所放入的槽,從而實(shí)現(xiàn)自動分割數(shù)據(jù)到不同的節(jié)點(diǎn)上。然后再將這些槽分配到不同的緩存節(jié)點(diǎn)中保存。

圖1:Redis 集群中的數(shù)據(jù)分片

如圖 1 所示,假設(shè)有三個緩存節(jié)點(diǎn)分別是 1、2、3。Redis Cluster 將存放緩存數(shù)據(jù)的槽(Slot)分別放入這三個節(jié)點(diǎn)中:

  • 緩存節(jié)點(diǎn) 1 存放的是(0-5000)Slot 的數(shù)據(jù)。
  • 緩存節(jié)點(diǎn) 2 存放的是(5001-10000)Slot 的數(shù)據(jù)。
  • 緩存節(jié)點(diǎn) 3 存放的是(10000-16383)Slot 的數(shù)據(jù)。

此時 Redis Client 需要根據(jù)一個 Key 獲取對應(yīng)的 Value 的數(shù)據(jù),首先通過 CRC16(key)%16383 計(jì)算出 Slot 的值,假設(shè)計(jì)算的結(jié)果是 5002。

將這個數(shù)據(jù)傳送給 Redis Cluster,集群接受到以后會到一個對照表中查找這個 Slot=5002 屬于那個緩存節(jié)點(diǎn)。

發(fā)現(xiàn)屬于“緩存節(jié)點(diǎn) 2”,于是順著紅線的方向調(diào)用緩存節(jié)點(diǎn) 2 中存放的 Key-Value 的內(nèi)容并且返回給 Redis Client。

分布式緩存節(jié)點(diǎn)之間的通訊

如果說 Redis Cluster 的虛擬槽算法解決的是數(shù)據(jù)拆分和存放的問題,那么存放緩存數(shù)據(jù)的節(jié)點(diǎn)之間是如何通訊的,就是接下來我們要討論的。

緩存節(jié)點(diǎn)中存放著緩存的數(shù)據(jù),在 Redis Cluster 的分布式部署下,緩存節(jié)點(diǎn)會被分配到一臺或者多臺服務(wù)器上。

圖 2:新上線的緩存節(jié)點(diǎn) 2 和緩存節(jié)點(diǎn) 1 進(jìn)行通訊

緩存節(jié)點(diǎn)的數(shù)目也有可能根據(jù)緩存數(shù)據(jù)量和支持的并發(fā)進(jìn)行擴(kuò)展。如圖 2 所示,假設(shè) Redis Cluster 中存在“緩存節(jié)點(diǎn) 1”,此時由于業(yè)務(wù)擴(kuò)展新增了“緩存節(jié)點(diǎn) 2”。

新加入的節(jié)點(diǎn)會通過 Gossip 協(xié)議向老節(jié)點(diǎn),發(fā)出一個“Meet 消息”。收到消息以后“緩存節(jié)點(diǎn) 1”,會禮貌地回復(fù)一個“Pong 消息”。

此后“緩存節(jié)點(diǎn) 2”會定期發(fā)送給“緩存節(jié)點(diǎn) 1” 一個“Ping 消息”,同樣的“緩存節(jié)點(diǎn) 1”每次都會回復(fù)“Pong 消息”。

上面這個例子說明了,在 Redis Cluster 中緩存節(jié)點(diǎn)之間是通過 Gossip 協(xié)議進(jìn)行通訊的。

其實(shí)節(jié)點(diǎn)之間通訊的目的是為了維護(hù)節(jié)點(diǎn)之間的元數(shù)據(jù)信息。這個元數(shù)據(jù)就是每個節(jié)點(diǎn)包含哪些數(shù)據(jù),是否出現(xiàn)故障。

節(jié)點(diǎn)之間通過 Gossip 協(xié)議不斷相互交互這些信息,就好像一群人在一起八卦一樣,沒有多久每個節(jié)點(diǎn)就知道其他所有節(jié)點(diǎn)的情況了,這個情況就是節(jié)點(diǎn)的元數(shù)據(jù)。

整個傳輸過程大致分為以下幾點(diǎn):

  • Redis Cluster 的每個緩存節(jié)點(diǎn)都會開通一個獨(dú)立的 TCP 通道,用于和其他節(jié)點(diǎn)通訊。
  • 有一個節(jié)點(diǎn)定時任務(wù),每隔一段時間會從系統(tǒng)中選出“發(fā)送節(jié)點(diǎn)”。這個“發(fā)送節(jié)點(diǎn)”按照一定頻率,例如:每秒 5 次,隨機(jī)向最久沒有通訊的節(jié)點(diǎn)發(fā)起 Ping 消息。
  • 接受到 Ping 消息的節(jié)點(diǎn)會使用 Pong 消息向“發(fā)送節(jié)點(diǎn)”進(jìn)行回復(fù)。

不斷重復(fù)上面行為,讓所有節(jié)點(diǎn)保持通訊。他們之間通訊是通過 Gossip 協(xié)議實(shí)現(xiàn)的。

從類型上來說其分為了四種,分別是:

  • Meet 消息,用于通知新節(jié)點(diǎn)加入。就好像上面例子中提到的新節(jié)點(diǎn)上線會給老節(jié)點(diǎn)發(fā)送 Meet 消息,表示有“新成員”加入。
  • Ping 消息,這個消息使用得最為頻繁,該消息中封裝了自身節(jié)點(diǎn)和其他節(jié)點(diǎn)的狀態(tài)數(shù)據(jù),有規(guī)律地發(fā)給其他節(jié)點(diǎn)。
  • Pong 消息,在接受到 Meet 和 Ping 消息以后,也將自己的數(shù)據(jù)狀態(tài)發(fā)給對方。同時也可以對集群中所有的節(jié)點(diǎn)發(fā)起廣播,告知大家的自身狀態(tài)。
  • Fail 消息,如果一個節(jié)點(diǎn)下線或者掛掉了,會向集群中廣播這個消息。

圖 3:Gossip 協(xié)議結(jié)構(gòu)

Gossip 協(xié)議的結(jié)構(gòu)如圖 3 所示,有其中 type 定義了消息的類型,例如:Meet、Ping、Pong、Fail 等消息。

另外有一個 myslots 的數(shù)組定義了節(jié)點(diǎn)負(fù)責(zé)的槽信息。每個節(jié)點(diǎn)發(fā)送 Gossip 協(xié)議給其他節(jié)點(diǎn)最重要的就是將該信息告訴其他節(jié)點(diǎn)。另外,消息體通過 clusterMsgData 對象傳遞消息征文。

請求分布式緩存的路由

對內(nèi),分布式緩存的節(jié)點(diǎn)通過 Gossip 協(xié)議互相發(fā)送消息,為了保證節(jié)點(diǎn)之間了解對方的情況。

那么對外來說,一個 Redis 客戶端如何通過分布式節(jié)點(diǎn)獲取緩存數(shù)據(jù),就是分布式緩存路由要解決的問題了。

上文提到了 Gossip 協(xié)議會將每個節(jié)點(diǎn)管理的槽信息發(fā)送給其他節(jié)點(diǎn),其中用到了 unsigned char myslots[CLUSTER_SLOTS/8] 這樣一個數(shù)組存放每個節(jié)點(diǎn)的槽信息。

myslots 屬性是一個二進(jìn)制位數(shù)組(bit array),其中 CLUSTER_SLOTS 為 16384。

這個數(shù)組的長度為 16384/8=2048 個字節(jié),由于每個字節(jié)包含 8 個 bit 位(二進(jìn)制位),所以共包含 16384 個 bit,也就是 16384 個二進(jìn)制位。

每個節(jié)點(diǎn)用 bit 來標(biāo)識自己是否擁有某個槽的數(shù)據(jù)。如圖 4 所示,假設(shè)這個圖表示節(jié)點(diǎn) A 所管理槽的情況。

圖 4:通過二進(jìn)制數(shù)組存放槽信息

0、1、2 三個數(shù)組下標(biāo)就表示 0、1、2 三個槽,如果對應(yīng)的二進(jìn)制值是 1,表示該節(jié)點(diǎn)負(fù)責(zé)存放 0、1、2 三個槽的數(shù)據(jù)。同理,后面的數(shù)組下標(biāo)位 0 就表示該節(jié)點(diǎn)不負(fù)責(zé)存放對應(yīng)槽的數(shù)據(jù)。

用二進(jìn)制存放的優(yōu)點(diǎn)是,判斷的效率高,例如對于編號為 1 的槽,節(jié)點(diǎn)只要判斷序列的第二位,時間復(fù)雜度為 O(1)。

圖 5:接受節(jié)點(diǎn)把節(jié)點(diǎn)槽的對應(yīng)信息保存在本地

如圖 5 所示,當(dāng)收到發(fā)送節(jié)點(diǎn)的節(jié)點(diǎn)槽信息以后,接受節(jié)點(diǎn)會將這些信息保存到本地的 clusterState 的結(jié)構(gòu)中,其中 Slots 的數(shù)組就是存放每個槽對應(yīng)哪些節(jié)點(diǎn)信息。

圖 6:ClusterStatus 結(jié)構(gòu)以及槽與節(jié)點(diǎn)的對應(yīng)

如圖 6 所示,ClusterState 中保存的 Slots 數(shù)組中每個下標(biāo)對應(yīng)一個槽,每個槽信息中對應(yīng)一個 clusterNode 也就是緩存的節(jié)點(diǎn)。

這些節(jié)點(diǎn)會對應(yīng)一個實(shí)際存在的 Redis 緩存服務(wù),包括 IP 和 Port 的信息。

Redis Cluster 的通訊機(jī)制實(shí)際上保證了每個節(jié)點(diǎn)都有其他節(jié)點(diǎn)和槽數(shù)據(jù)的對應(yīng)關(guān)系。

Redis 的客戶端無論訪問集群中的哪個節(jié)點(diǎn)都可以路由到對應(yīng)的節(jié)點(diǎn)上,因?yàn)槊總€節(jié)點(diǎn)都有一份 ClusterState,它記錄了所有槽和節(jié)點(diǎn)的對應(yīng)關(guān)系。

下面來看看 Redis 客戶端是如何通過路由來調(diào)用緩存節(jié)點(diǎn)的:

圖 7:MOVED 重定向請求

如圖 7 所示,Redis 客戶端通過 CRC16(key)%16383 計(jì)算出 Slot 的值,發(fā)現(xiàn)需要找“緩存節(jié)點(diǎn) 1”讀/寫數(shù)據(jù),但是由于緩存數(shù)據(jù)遷移或者其他原因?qū)е逻@個對應(yīng)的 Slot 的數(shù)據(jù)被遷移到了“緩存節(jié)點(diǎn) 2”上面。

那么這個時候 Redis 客戶端就無法從“緩存節(jié)點(diǎn) 1”中獲取數(shù)據(jù)了。

但是由于“緩存節(jié)點(diǎn) 1”中保存了所有集群中緩存節(jié)點(diǎn)的信息,因此它知道這個 Slot 的數(shù)據(jù)在“緩存節(jié)點(diǎn) 2”中保存,因此向 Redis 客戶端發(fā)送了一個 MOVED 的重定向請求。

這個請求告訴其應(yīng)該訪問的“緩存節(jié)點(diǎn) 2”的地址。Redis 客戶端拿到這個地址,繼續(xù)訪問“緩存節(jié)點(diǎn) 2”并且拿到數(shù)據(jù)。

上面的例子說明了,數(shù)據(jù) Slot 從“緩存節(jié)點(diǎn) 1”已經(jīng)遷移到“緩存節(jié)點(diǎn) 2”了,那么客戶端可以直接找“緩存節(jié)點(diǎn) 2”要數(shù)據(jù)。

那么如果兩個緩存節(jié)點(diǎn)正在做節(jié)點(diǎn)的數(shù)據(jù)遷移,此時客戶端請求會如何處理呢?

圖 8:ASK 重定向請求

如圖 8 所示,Redis 客戶端向“緩存節(jié)點(diǎn) 1”發(fā)出請求,此時“緩存節(jié)點(diǎn) 1”正向“緩存節(jié)點(diǎn) 2”遷移數(shù)據(jù),如果沒有命中對應(yīng)的 Slot,它會返回客戶端一個 ASK 重定向請求并且告訴“緩存節(jié)點(diǎn) 2”的地址。

客戶端向“緩存節(jié)點(diǎn) 2”發(fā)送 Asking 命令,詢問需要的數(shù)據(jù)是否在“緩存節(jié)點(diǎn) 2”上,“緩存節(jié)點(diǎn) 2”接到消息以后返回數(shù)據(jù)是否存在的結(jié)果。

緩存節(jié)點(diǎn)的擴(kuò)展和收縮

作為分布式部署的緩存節(jié)點(diǎn)總會遇到緩存擴(kuò)容和緩存故障的問題。這就會導(dǎo)致緩存節(jié)點(diǎn)的上線和下線的問題。

由于每個節(jié)點(diǎn)中保存著槽數(shù)據(jù),因此當(dāng)緩存節(jié)點(diǎn)數(shù)出現(xiàn)變動時,這些槽數(shù)據(jù)會根據(jù)對應(yīng)的虛擬槽算法被遷移到其他的緩存節(jié)點(diǎn)上。

圖 9:分布式緩存擴(kuò)容

如圖 9 所示,集群中本來存在“緩存節(jié)點(diǎn) 1”和“緩存節(jié)點(diǎn) 2”,此時“緩存節(jié)點(diǎn) 3”上線了并且加入到集群中。

此時根據(jù)虛擬槽的算法,“緩存節(jié)點(diǎn) 1”和“緩存節(jié)點(diǎn) 2”中對應(yīng)槽的數(shù)據(jù)會應(yīng)該新節(jié)點(diǎn)的加入被遷移到“緩存節(jié)點(diǎn) 3”上面。

針對節(jié)點(diǎn)擴(kuò)容,新建立的節(jié)點(diǎn)需要運(yùn)行在集群模式下,因此新建節(jié)點(diǎn)的配置最好與集群內(nèi)其他節(jié)點(diǎn)配置保持一致。

新節(jié)點(diǎn)加入到集群的時候,作為孤兒節(jié)點(diǎn)是沒有和其他節(jié)點(diǎn)進(jìn)行通訊的。因此,其會采用 cluster meet 命令加入到集群中。

在集群中任意節(jié)點(diǎn)執(zhí)行 cluster meet 命令讓新節(jié)點(diǎn)加入進(jìn)來。假設(shè)新節(jié)點(diǎn)是 192.168.1.1 5002,老節(jié)點(diǎn)是 192.168.1.1 5003,那么運(yùn)行以下命令將新節(jié)點(diǎn)加入到集群中。

  1. 192.168.1.1 5003> cluster meet 192.168.1.1 5002 

這個是由老節(jié)點(diǎn)發(fā)起的,有點(diǎn)老成員歡迎新成員加入的意思。新節(jié)點(diǎn)剛剛建立沒有建立槽對應(yīng)的數(shù)據(jù),也就是說沒有緩存任何數(shù)據(jù)。

如果這個節(jié)點(diǎn)是主節(jié)點(diǎn),需要對其進(jìn)行槽數(shù)據(jù)的擴(kuò)容;如果這個節(jié)點(diǎn)是從節(jié)點(diǎn),就需要同步主節(jié)點(diǎn)上的數(shù)據(jù)??傊褪且綌?shù)據(jù)。

圖 10:節(jié)點(diǎn)遷移槽數(shù)據(jù)的過程

如圖 10 所示,由客戶端發(fā)起節(jié)點(diǎn)之間的槽數(shù)據(jù)遷移,數(shù)據(jù)從源節(jié)點(diǎn)往目標(biāo)節(jié)點(diǎn)遷移:

  • 客戶端對目標(biāo)節(jié)點(diǎn)發(fā)起準(zhǔn)備導(dǎo)入槽數(shù)據(jù)的命令,讓目標(biāo)節(jié)點(diǎn)準(zhǔn)備好導(dǎo)入槽數(shù)據(jù)。這里使用 cluster setslot {slot} importing {sourceNodeId} 命令。
  • 之后對源節(jié)點(diǎn)發(fā)起送命令,讓源節(jié)點(diǎn)準(zhǔn)備遷出對應(yīng)的槽數(shù)據(jù)。使用命令 cluster setslot {slot} importing {sourceNodeId}。
  • 此時源節(jié)點(diǎn)準(zhǔn)備遷移數(shù)據(jù)了,在遷移之前把要遷移的數(shù)據(jù)獲取出來。通過命令 cluster getkeysinslot {slot} {count}。Count 表示遷移的 Slot 的個數(shù)。
  • 然后在源節(jié)點(diǎn)上執(zhí)行,migrate {targetIP} {targetPort} “” 0 {timeout} keys{keys} 命令,把獲取的鍵通過流水線批量遷移到目標(biāo)節(jié)點(diǎn)。
  • 重復(fù) 3 和 4 兩步不斷將數(shù)據(jù)遷移到目標(biāo)節(jié)點(diǎn)。目標(biāo)節(jié)點(diǎn)獲取遷移的數(shù)據(jù)。
  • 完成數(shù)據(jù)遷移以后目標(biāo)節(jié)點(diǎn),通過 cluster setslot {slot} node {targetNodeId} 命令通知對應(yīng)的槽被分配到目標(biāo)節(jié)點(diǎn),并且廣播這個信息給全網(wǎng)的其他主節(jié)點(diǎn),更新自身的槽節(jié)點(diǎn)對應(yīng)表。

既然有緩存服務(wù)器的上線操作,那么也有下線的操作。下線操作正好和上線操作相反,將要下線緩存節(jié)點(diǎn)的槽數(shù)據(jù)分配到其他的緩存主節(jié)點(diǎn)中。

遷移的過程也與上線操作類似,不同的是下線的時候需要通知全網(wǎng)的其他節(jié)點(diǎn)忘記自己,此時通過命令 cluster forget{downNodeId} 通知其他的節(jié)點(diǎn)。

當(dāng)節(jié)點(diǎn)收到 forget 命令以后會將這個下線節(jié)點(diǎn)放到僅用列表中,那么之后就不用再向這個節(jié)點(diǎn)發(fā)送 Gossip 的 Ping 消息了。

不過這個僅用表的超時時間是 60 秒,超過了這個時間,依舊還會對這個節(jié)點(diǎn)發(fā)起 Ping 消息。

不過可以使用 redis-trib.rb del-node{host:port} {donwNodeId} 命令幫助我們完成下線操作。

尤其是下線的節(jié)點(diǎn)是主節(jié)點(diǎn)的情況下,會安排對應(yīng)的從節(jié)點(diǎn)接替主節(jié)點(diǎn)的位置。

故障發(fā)現(xiàn)和恢復(fù)

前面在談到緩存節(jié)點(diǎn)擴(kuò)展和收縮是提到,緩存節(jié)點(diǎn)收縮時會有一個下線的動作。

有些時候是為了節(jié)約資源,或者是計(jì)劃性的下線,但更多時候是節(jié)點(diǎn)出現(xiàn)了故障導(dǎo)致下線。

針對下線故障來說有兩種下線的確定方式:

主觀下線:當(dāng)節(jié)點(diǎn) 1 向節(jié)點(diǎn) 2 例行發(fā)送 Ping 消息的時候,如果節(jié)點(diǎn) 2 正常工作就會返回 Pong 消息,同時會記錄節(jié)點(diǎn) 1 的相關(guān)信息。

同時接受到 Pong 消息以后節(jié)點(diǎn) 1 也會更新最近一次與節(jié)點(diǎn) 2 通訊的時間。

如果此時兩個節(jié)點(diǎn)由于某種原因斷開連接,過一段時間以后節(jié)點(diǎn) 1 還會主動連接節(jié)點(diǎn) 2,如果一直通訊失敗,節(jié)點(diǎn) 1 中就無法更新與節(jié)點(diǎn) 2 最后通訊時間了。

此時節(jié)點(diǎn) 1 的定時任務(wù)檢測到與節(jié)點(diǎn) 2 最好通訊的時間超過了 cluster-node-timeout 的時候,就會更新本地節(jié)點(diǎn)狀態(tài),把節(jié)點(diǎn) 2 更新為主觀下線。

這里的 cluster-node-timeout 是節(jié)點(diǎn)掛掉被發(fā)現(xiàn)的超時時間,如果超過這個時間還沒有獲得節(jié)點(diǎn)返回的 Pong 消息就認(rèn)為該節(jié)點(diǎn)掛掉了。

這里的主觀下線指的是,節(jié)點(diǎn) 1 主觀的認(rèn)為節(jié)點(diǎn) 2 沒有返回 Pong 消息,因此認(rèn)為節(jié)點(diǎn) 2 下線。

只是節(jié)點(diǎn) 1 的主觀認(rèn)為,有可能是節(jié)點(diǎn) 1 與節(jié)點(diǎn) 2 之間的網(wǎng)絡(luò)斷開了,但是其他的節(jié)點(diǎn)依舊可以和節(jié)點(diǎn) 2 進(jìn)行通訊,因此主觀下線并不能代表某個節(jié)點(diǎn)真的下線了。

客觀下線:由于 Redis Cluster 的節(jié)點(diǎn)不斷地與集群內(nèi)的節(jié)點(diǎn)進(jìn)行通訊,下線信息也會通過 Gossip 消息傳遍所有節(jié)點(diǎn)。

因此集群內(nèi)的節(jié)點(diǎn)會不斷收到下線報告,當(dāng)半數(shù)以上持有槽的主節(jié)點(diǎn)標(biāo)記了某個節(jié)點(diǎn)是主觀下線時,便會觸發(fā)客觀下線的流程。

也就是說當(dāng)集群內(nèi)的半數(shù)以上的主節(jié)點(diǎn),認(rèn)為某個節(jié)點(diǎn)主觀下線了,才會啟動這個流程。

這個流程有一個前提,就是直針對主節(jié)點(diǎn),如果是從節(jié)點(diǎn)就會忽略。也就是說集群中的節(jié)點(diǎn)每次接受到其他節(jié)點(diǎn)的主觀下線是都會做以下的事情。

將主觀下線的報告保存到本地的 ClusterNode 的結(jié)構(gòu)中,并且針對主觀下線報告的時效性進(jìn)行檢查,如果超過 cluster-node-timeout*2 的時間,就忽略這個報告。

否則就記錄報告內(nèi)容,并且比較被標(biāo)記下線的主觀節(jié)點(diǎn)的報告數(shù)量大于等于持有槽的主節(jié)點(diǎn)數(shù)量的時候,將其標(biāo)記為客觀下線。

同時向集群中廣播一條 Fail 消息,通知所有的節(jié)點(diǎn)將故障節(jié)點(diǎn)標(biāo)記為客觀下線,這個消息指包含故障節(jié)點(diǎn)的 ID。

此后,群內(nèi)所有的節(jié)點(diǎn)都會標(biāo)記這個節(jié)點(diǎn)為客觀下線,通知故障節(jié)點(diǎn)的從節(jié)點(diǎn)出發(fā)故障轉(zhuǎn)移的流程,也就是故障的恢復(fù)。

說白了,客觀下線就是整個集群中有一半的節(jié)點(diǎn)都認(rèn)為某節(jié)點(diǎn)主觀下線了,那么這個節(jié)點(diǎn)就被標(biāo)記為客觀下線了。

如果某個主節(jié)點(diǎn)被認(rèn)為客觀下線了,那么需要從它的從節(jié)點(diǎn)中選出一個節(jié)點(diǎn)替代主節(jié)點(diǎn)的位置。

此時下線主節(jié)點(diǎn)的所有從節(jié)點(diǎn)都擔(dān)負(fù)著恢復(fù)義務(wù),這些從節(jié)點(diǎn)會定時監(jiān)測主節(jié)點(diǎn)是否下線。

一旦發(fā)現(xiàn)下線會走如下的恢復(fù)流程:

①資格檢查,每個節(jié)點(diǎn)都會檢查與主節(jié)點(diǎn)斷開的時間。如果這個時間超過了 cluster-node-timeout*cluster-slave-validity-factor(從節(jié)點(diǎn)有效因子,默認(rèn)為 10),那么就沒有故障轉(zhuǎn)移的資格。

也就是說這個從節(jié)點(diǎn)和主節(jié)點(diǎn)斷開的太久了,很久沒有同步主節(jié)點(diǎn)的數(shù)據(jù)了,不適合成為新的主節(jié)點(diǎn),因?yàn)槌蔀橹鞴?jié)點(diǎn)以后其他的從節(jié)點(diǎn)回同步自己的數(shù)據(jù)。

②觸發(fā)選舉,通過了上面資格的從節(jié)點(diǎn)都可以觸發(fā)選舉。但是出發(fā)選舉是有先后順序的,這里按照復(fù)制偏移量的大小來判斷。

這個偏移量記錄了執(zhí)行命令的字節(jié)數(shù)。主服務(wù)器每次向從服務(wù)器傳播 N 個字節(jié)時就會將自己的復(fù)制偏移量+N,從服務(wù)在接收到主服務(wù)器傳送來的 N 個字節(jié)的命令時,就將自己的復(fù)制偏移量+N。

復(fù)制偏移量越大說明從節(jié)點(diǎn)延遲越低,也就是該從節(jié)點(diǎn)和主節(jié)點(diǎn)溝通更加頻繁,該從節(jié)點(diǎn)上面的數(shù)據(jù)也會更新一些,因此復(fù)制偏移量大的從節(jié)點(diǎn)會率先發(fā)起選舉。

③發(fā)起選舉,首先每個主節(jié)點(diǎn)會去更新配置紀(jì)元(clusterNode.configEpoch),這個值是不斷增加的整數(shù)。

在節(jié)點(diǎn)進(jìn)行 Ping/Pong 消息交互式也會更新這個值,它們都會將最大的值更新到自己的配置紀(jì)元中。

這個值記錄了每個節(jié)點(diǎn)的版本和整個集群的版本。每當(dāng)發(fā)生重要事情的時候,例如:出現(xiàn)新節(jié)點(diǎn),從節(jié)點(diǎn)精選。都會增加全局的配置紀(jì)元并且賦給相關(guān)的主節(jié)點(diǎn),用來記錄這個事件。

說白了更新這個值目的是,保證所有主節(jié)點(diǎn)對這件“大事”保持一致。大家都統(tǒng)一成一個配置紀(jì)元(一個整數(shù)),表示大家都知道這個“大事”了。

更新完配置紀(jì)元以后,會想群內(nèi)發(fā)起廣播選舉的消息(FAILOVER_AUTH_REQUEST)。并且保證每個從節(jié)點(diǎn)在一次配置紀(jì)元中只能發(fā)起一次選舉。

④投票選舉,參與投票的只有主節(jié)點(diǎn),從節(jié)點(diǎn)沒有投票權(quán),超過半數(shù)的主節(jié)點(diǎn)通過某一個節(jié)點(diǎn)成為新的主節(jié)點(diǎn)時投票完成。

如果在 cluster-node-timeout*2 的時間內(nèi)從節(jié)點(diǎn)沒有獲得足夠數(shù)量的票數(shù),本次選舉作廢,進(jìn)行第二輪選舉。

這里每個候選的從節(jié)點(diǎn)會收到其他主節(jié)點(diǎn)投的票。在第2步領(lǐng)先的從節(jié)點(diǎn)通常此時會獲得更多的票,因?yàn)樗|發(fā)選舉的時間更早一些。

獲得票的機(jī)會更大,也是由于它和原主節(jié)點(diǎn)延遲少,理論上數(shù)據(jù)會更加新一點(diǎn)。

⑤當(dāng)滿足投票條件的從節(jié)點(diǎn)被選出來以后,會觸發(fā)替換主節(jié)點(diǎn)的操作。新的主節(jié)點(diǎn)別選出以后,刪除原主節(jié)點(diǎn)負(fù)責(zé)的槽數(shù)據(jù),把這些槽數(shù)據(jù)添加到自己節(jié)點(diǎn)上。

并且廣播讓其他的節(jié)點(diǎn)都知道這件事情,新的主節(jié)點(diǎn)誕生了。

總結(jié)

本文通過 Redis Cluster 提供了分布式緩存的方案為出發(fā)點(diǎn),針對此方案中緩存節(jié)點(diǎn)的分區(qū)方式進(jìn)行了描述。

虛擬槽的分區(qū)算法,將整塊數(shù)據(jù)分配到了不同的緩存節(jié)點(diǎn),通過 Slot 和 Node 的對應(yīng)關(guān)系讓數(shù)據(jù)找到節(jié)點(diǎn)的位置。

對于分布式部署的節(jié)點(diǎn),需要通過 Gossip 協(xié)議進(jìn)行 Ping、Pong、Meet、Fail 的通訊,達(dá)到互通有無的目的。

當(dāng)客戶端調(diào)用緩存節(jié)點(diǎn)數(shù)據(jù)的時候通過 MOVED 和 ASKED 重定向請求找到正確的緩存節(jié)點(diǎn)。

并且介紹了在緩存擴(kuò)容和收縮時需要注意的處理流程,以及數(shù)據(jù)遷移的方式。

最后,講述如何發(fā)現(xiàn)故障(主觀下線和客觀下線)以及如何恢復(fù)故障(選舉節(jié)點(diǎn))的處理流程。

【51CTO原創(chuàng)稿件,合作站點(diǎn)轉(zhuǎn)載請注明原文作者和出處為51CTO.com】

 

責(zé)任編輯:武曉燕 來源: 51CTO技術(shù)棧
相關(guān)推薦

2021-01-18 11:27:03

Istio架構(gòu)云環(huán)境

2020-12-18 08:28:13

Redis數(shù)據(jù)數(shù)據(jù)庫

2020-04-24 09:14:21

術(shù)語網(wǎng)絡(luò)安全黑客

2021-09-22 10:15:52

裁員選擇公司個人發(fā)展

2013-06-20 11:11:00

程序員經(jīng)理

2021-08-26 14:26:25

Java代碼集合

2021-12-03 11:57:27

代碼##語言

2020-03-20 08:00:32

代碼程序員追求

2022-10-24 08:41:52

集群配置編輯

2023-06-10 23:09:40

Redis場景內(nèi)存

2024-01-15 09:15:52

parallel語句函數(shù)

2021-08-17 11:02:24

isEmptyisBlankJava

2020-07-13 08:40:48

同事代碼

2021-09-26 08:42:51

RedisGeo 類型數(shù)據(jù)類型

2021-04-27 07:52:19

StarterSpring Boot配置

2022-05-27 16:03:37

ReactDanVue

2021-06-29 10:02:04

亞馬遜機(jī)器解雇

2023-11-02 08:27:29

2022-09-05 11:28:07

數(shù)據(jù)分析活動分析

2022-06-16 14:07:26

Java代碼代碼review
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號