1. 程式人生 > 資料庫 >redis叢集規範詳解

redis叢集規範詳解

本文件翻譯自 http://redis.io/topics/cluster-spec 。

引言

這個文件是正在開發中的 Redis 叢集功能的規範(specification)文件, 文件分為兩個部分:

第一部分介紹目前已經在 unstable 分支中實現了的那些功能。
第二部分介紹目前仍未實現的那些功能。

文件各個部分的內容可能會隨著叢集功能的設計修改而發生改變, 其中, 未實現功能發生修改的機率比已實現功能發生修改的機率要高。

這個規範包含了編寫客戶端庫(client library)所需的全部知識, 不過請注意, 這裡列出的一部分細節可能會在未來發生變化。

什麼是 Redis 叢集?

Redis 叢集是一個分散式(distributed)、容錯(fault-tolerant)的 Redis 實現, 叢集可以使用的功能是普通單機 Redis 所能使用的功能的一個子集(subset)。

Redis 叢集中不存在中心(central)節點或者代理(proxy)節點, 叢集的其中一個主要設計目標是達到線性可擴充套件性(linear scalability)。

Redis 叢集為了保證一致性(consistency)而犧牲了一部分容錯性: 系統會在保證對網路斷線(net split)和節點失效(node failure)具有有限(limited)抵抗力的前提下, 儘可能地保持資料的一致性。

叢集將節點失效視為網路斷線的其中一種特殊情況。

叢集的容錯功能是通過使用主節點(master)和從節點(slave)兩種角色(role)的節點(node)來實現的:

主節點和從節點使用完全相同的伺服器實現, 它們的功能(functionally)也完全一樣, 但從節點通常僅用於替換失效的主節點。

不過, 如果不需要保證“先寫入,後讀取”操作的一致性(read-after-write consistency), 那麼可以使用從節點來執行只讀查詢。

Redis 叢集實現的功能子集

Redis 叢集實現了單機 Redis 中, 所有處理單個數據庫鍵的命令。

針對多個數據庫鍵的複雜計算操作, 比如集合的並集操作、合集操作沒有被實現, 那些理論上需要使用多個節點的多個數據庫鍵才能完成的命令也沒有被實現。

在將來, 使用者也許可以通過 MIGRATE COPY 命令, 在叢集的計算節點(computation node)中執行鍼對多個數據庫鍵的只讀操作, 但叢集本身不會去實現那些需要將多個數據庫鍵在多個節點中移來移去的複雜多鍵命令。

Redis 叢集不像單機 Redis 那樣支援多資料庫功能, 叢集只使用預設的 0 號資料庫, 並且不能使用 SELECT 命令。

Redis 叢集協議中的客戶端和伺服器

Redis 叢集中的節點有以下責任:

持有鍵值對資料。
記錄叢集的狀態,包括鍵到正確節點的對映(mapping keys to right nodes)。
自動發現其他節點,識別工作不正常的節點,並在有需要時,在從節點中選舉出新的主節點。

為了執行以上列出的任務, 叢集中的每個節點都與其他節點建立起了“叢集連線(cluster bus)”, 該連線是一個 TCP 連線, 使用二進位制協議進行通訊。

節點之間使用 Gossip 協議 來進行以下工作:

傳播(propagate)關於叢集的資訊,以此來發現新的節點。
向其他節點發送 PING 資料包,以此來檢查目標節點是否正常運作。
在特定事件發生時,傳送叢集資訊。

除此之外, 叢集連線還用於在叢集中釋出或訂閱資訊。

因為叢集節點不能代理(proxy)命令請求, 所以客戶端應該在節點返回 -MOVED 或者 -ASK 轉向(redirection)錯誤時, 自行將命令請求轉發至其他節點。

因為客戶端可以自由地向叢集中的任何一個節點發送命令請求, 並可以在有需要時, 根據轉向錯誤所提供的資訊, 將命令轉發至正確的節點, 所以在理論上來說, 客戶端是無須儲存叢集狀態資訊的。

不過, 如果客戶端可以將鍵和節點之間的對映資訊儲存起來, 可以有效地減少可能出現的轉向次數, 籍此提升命令執行的效率。

鍵分佈模型

Redis 叢集的鍵空間被分割為 16384 個槽(slot), 叢集的最大節點數量也是 16384 個。

推薦的最大節點數量為 1000 個左右。

每個主節點都負責處理 16384 個雜湊槽的其中一部分。

當我們說一個叢集處於“穩定”(stable)狀態時, 指的是叢集沒有在執行重配置(reconfiguration)操作, 每個雜湊槽都只由一個節點進行處理。

重配置指的是將某個/某些槽從一個節點移動到另一個節點。

一個主節點可以有任意多個從節點, 這些從節點用於在主節點發生網路斷線或者節點失效時, 對主節點進行替換。

以下是負責將鍵對映到槽的演算法:

HASH_SLOT = CRC16(key) mod 16384

以下是該演算法所使用的引數:

演算法的名稱: XMODEM (又稱 ZMODEM 或者 CRC-16/ACORN)
結果的長度: 16 位
多項數(poly): 1021 (也即是 x16 + x12 + x5 + 1)
初始化值: 0000
反射輸入位元組(Reflect Input byte): False
發射輸出 CRC (Reflect Output CRC): False
用於 CRC 輸出值的異或常量(Xor constant to output CRC): 0000
該演算法對於輸入 "123456789" 的輸出: 31C3

附錄 A 中給出了叢集所使用的 CRC16 演算法的實現。

CRC16 演算法所產生的 16 位輸出中的 14 位會被用到。
在我們的測試中, CRC16 演算法可以很好地將各種不同型別的鍵平穩地分佈到 16384 個槽裡面。

叢集節點屬性

每個節點在叢集中都有一個獨一無二的 ID , 該 ID 是一個十六進位制表示的 160 位隨機數, 在節點第一次啟動時由 /dev/urandom 生成。

節點會將它的 ID 儲存到配置檔案, 只要這個配置檔案不被刪除, 節點就會一直沿用這個 ID 。

節點 ID 用於標識叢集中的每個節點。 一個節點可以改變它的 IP 和埠號, 而不改變節點 ID 。 叢集可以自動識別出 IP/埠號的變化, 並將這一資訊通過 Gossip 協議廣播給其他節點知道。

以下是每個節點都有的關聯資訊, 並且節點會將這些資訊傳送給其他節點:

節點所使用的 IP 地址和 TCP 埠號。
節點的標誌(flags)。
節點負責處理的雜湊槽。
節點最近一次使用叢集連線傳送 PING 資料包(packet)的時間。
節點最近一次在回覆中接收到 PONG 資料包的時間。
叢集將該節點標記為下線的時間。
該節點的從節點數量。
如果該節點是從節點的話,那麼它會記錄主節點的節點 ID 。 如果這是一個主節點的話,那麼主節點 ID 這一欄的值為 0000000 。

以上資訊的其中一部分可以通過向叢集中的任意節點(主節點或者從節點都可以)傳送 CLUSTER NODES 命令來獲得。

以下是一個向叢集中的主節點發送 CLUSTER NODES 命令的例子, 該叢集由三個節點組成:

$ redis-cli cluster nodes
d1861060fe6a534d42d8a19aeb36600e18785e04 :0 myself - 0 1318428930 connected 0-1364
3886e65cc906bfd9b1f7e7bde468726a052d1dae 127.0.0.1:6380 master - 1318428930 1318428931 connected 1365-2729
d289c575dcbc4bdd2931585fd4339089e461a27d 127.0.0.1:6381 master - 1318428931 1318428931 connected 2730-4095

在上面列出的三行資訊中, 從左到右的各個域分別是: 節點 ID , IP 地址和埠號, 標誌(flag), 最後傳送 PING 的時間, 最後接收 PONG 的時間, 連線狀態, 節點負責處理的槽。

節點握手(已實現)

節點總是應答(accept)來自叢集連線埠的連線請求, 並對接收到的 PING 資料包進行回覆, 即使這個 PING 資料包來自不可信的節點。

然而, 除了 PING 之外, 節點會拒絕其他所有並非來自叢集節點的資料包。

要讓一個節點承認另一個節點同屬於一個叢集, 只有以下兩種方法:

一個節點可以通過向另一個節點發送 MEET 資訊, 來強制讓接收資訊的節點承認傳送資訊的節點為叢集中的一份子。 一個節點僅在管理員顯式地向它傳送 CLUSTER MEET ip port 命令時, 才會向另一個節點發送 MEET 資訊。
另外, 如果一個可信節點向另一個節點傳播第三者節點的資訊, 那麼接收資訊的那個節點也會將第三者節點識別為叢集中的一份子。 也即是說, 如果 A 認識 B , B 認識 C , 並且 B 向 A 傳播關於 C 的資訊, 那麼 A 也會將 C 識別為叢集中的一份子, 並嘗試連線 C 。

這意味著如果我們將一個/一些新節點新增到一個叢集中, 那麼這個/這些新節點最終會和叢集中已有的其他所有節點連線起來。

這說明只要管理員使用 CLUSTER MEET 命令顯式地指定了可信關係, 叢集就可以自動發現其他節點。

這種節點識別機制通過防止不同的 Redis 叢集因為 IP 地址變更或者其他網路事件的發生而產生意料之外的聯合(mix), 從而使得叢集更具健壯性。

當節點的網路連線斷開時, 它會主動連線其他已知的節點。

MOVED 轉向

一個 Redis 客戶端可以向叢集中的任意節點(包括從節點)傳送命令請求。 節點會對命令請求進行分析, 如果該命令是叢集可以執行的命令, 那麼節點會查詢這個命令所要處理的鍵所在的槽。

如果要查詢的雜湊槽正好就由接收到命令的節點負責處理, 那麼節點就直接執行這個命令。
另一方面, 如果所查詢的槽不是由該節點處理的話, 節點將檢視自身內部所儲存的雜湊槽到節點 ID 的對映記錄, 並向客戶

端回覆一個 MOVED 錯誤。

以下是一個 MOVED 錯誤的例子:

GET x
-MOVED 3999 127.0.0.1:6381

錯誤資訊包含鍵 x 所屬的雜湊槽 3999 , 以及負責處理這個槽的節點的 IP 和埠號 127.0.0.1:6381 。 客戶端需要根據這個 IP 和埠號, 向所屬的節點重新發送一次 GET 命令請求。

注意, 即使客戶端在重新發送 GET 命令之前, 等待了非常久的時間, 以至於叢集又再次更改了配置, 使得節點 127.0.0.1:6381 已經不再處理槽 3999 , 那麼當客戶端向節點 127.0.0.1:6381 傳送 GET 命令的時候, 節點將再次向客戶端返回 MOVED 錯誤, 指示現在負責處理槽 3999 的節點。

雖然我們用 ID 來標識叢集中的節點, 但是為了讓客戶端的轉向操作儘可能地簡單, 節點在 MOVED 錯誤中直接返回目標節點的 IP 和埠號, 而不是目標節點的 ID 。

雖然不是必須的, 但一個客戶端應該記錄(memorize)下“槽 3999 由節點 127.0.0.1:6381 負責處理“這一資訊, 這樣當再次有命令需要對槽 3999 執行時, 客戶端就可以加快尋找正確節點的速度。

注意, 當叢集處於穩定狀態時, 所有客戶端最終都會儲存有一個雜湊槽至節點的對映記錄(map of hash slots to nodes), 使得叢集非常高效: 客戶端可以直接向正確的節點發送命令請求, 無須轉向、代理或者其他任何可能發生單點故障(single point failure)的實體(entiy)。

除了 MOVED 轉向錯誤之外, 一個客戶端還應該可以處理稍後介紹的 ASK 轉向錯誤。

叢集線上重配置(live reconfiguration)

Redis 叢集支援在叢集執行的過程中新增或者移除節點。

實際上, 節點的新增操作和節點的刪除操作可以抽象成同一個操作, 那就是, 將雜湊槽從一個節點移動到另一個節點:

新增一個新節點到叢集, 等於將其他已存在節點的槽移動到一個空白的新節點裡面。

從叢集中移除一個節點, 等於將被移除節點的所有槽移動到叢集的其他節點上面去。

因此, 實現 Redis 叢集線上重配置的核心就是將槽從一個節點移動到另一個節點的能力。 因為一個雜湊槽實際上就是一些鍵的集合, 所以 Redis 叢集在重雜湊(rehash)時真正要做的, 就是將一些鍵從一個節點移動到另一個節點。

要理解 Redis 叢集如何將槽從一個節點移動到另一個節點, 我們需要對 CLUSTER 命令的各個子命令進行介紹, 這些命理負責管理叢集節點的槽轉換表(slots translation table)。

以下是 CLUSTER 命令可用的子命令:

CLUSTER ADDSLOTS slot1 [slot2] ... [slotN]
CLUSTER DELSLOTS slot1 [slot2] ... [slotN]
CLUSTER SETSLOT slot NODE node
CLUSTER SETSLOT slot MIGRATING node
CLUSTER SETSLOT slot IMPORTING node

最開頭的兩條命令 ADDSLOTS 和 DELSLOTS 分別用於向節點指派(assign)或者移除節點, 當槽被指派或者移除之後, 節點會將這一資訊通過 Gossip 協議傳播到整個叢集。 ADDSLOTS 命令通常在新建立叢集時, 作為一種快速地將各個槽指派給各個節點的手段來使用。

CLUSTER SETSLOT slot NODE node 子命令可以將指定的槽 slot 指派給節點 node 。

至於 CLUSTER SETSLOT slot MIGRATING node 命令和 CLUSTER SETSLOT slot IMPORTING node 命令, 前者用於將給定節點 node 中的槽 slot 遷移出節點, 而後者用於將給定槽 slot 匯入到節點 node :

當一個槽被設定為 MIGRATING 狀態時, 原來持有這個槽的節點仍然會繼續接受關於這個槽的命令請求, 但只有命令所處理的鍵仍然存在於節點時, 節點才會處理這個命令請求。

如果命令所使用的鍵不存在與該節點, 那麼節點將向客戶端返回一個 -ASK 轉向(redirection)錯誤, 告知客戶端, 要將命令請求傳送到槽的遷移目標節點。

當一個槽被設定為 IMPORTING 狀態時, 節點僅在接收到 ASKING 命令之後, 才會接受關於這個槽的命令請求。

如果客戶端沒有向節點發送 ASKING 命令, 那麼節點會使用 -MOVED 轉向錯誤將命令請求轉向至真正負責處理這個槽的節點。

上面關於 MIGRATING 和 IMPORTING 的說明有些難懂, 讓我們用一個實際的例項來說明一下。

假設現在, 我們有 A 和 B 兩個節點, 並且我們想將槽 8 從節點 A 移動到節點 B , 於是我們:

向節點 B 傳送命令 CLUSTER SETSLOT 8 IMPORTING A
向節點 A 傳送命令 CLUSTER SETSLOT 8 MIGRATING B

每當客戶端向其他節點發送關於雜湊槽 8 的命令請求時, 這些節點都會向客戶端返回指向節點 A 的轉向資訊:

如果命令要處理的鍵已經存在於槽 8 裡面, 那麼這個命令將由節點 A 處理。
如果命令要處理的鍵未存在於槽 8 裡面(比如說,要向槽新增一個新的鍵), 那麼這個命令由節點 B 處理。

這種機制將使得節點 A 不再建立關於槽 8 的任何新鍵。

與此同時, 一個特殊的客戶端 redis-trib 以及 Redis 叢集配置程式(configuration utility)會將節點 A 中槽 8 裡面的鍵移動到節點 B 。

鍵的移動操作由以下兩個命令執行:

CLUSTER GETKEYSINSLOT slot count

上面的命令會讓節點返回 count 個 slot 槽中的鍵, 對於命令所返回的每個鍵, redis-trib 都會向節點 A 傳送一條 MIGRATE 命令, 該命令會將所指定的鍵原子地(atomic)從節點 A 移動到節點 B (在移動鍵期間,兩個節點都會處於阻塞狀態,以免出現競爭條件)。

以下為 MIGRATE 命令的運作原理:

MIGRATE target_host target_port key target_database id timeout

執行 MIGRATE 命令的節點會連線到 target 節點, 並將序列化後的 key 資料傳送給 target , 一旦 target 返回 OK , 節點就將自己的 key 從資料庫中刪除。

從一個外部客戶端的視角來看, 在某個時間點上, 鍵 key 要麼存在於節點 A , 要麼存在於節點 B , 但不會同時存在於節點 A 和節點 B 。

因為 Redis 叢集只使用 0 號資料庫, 所以當 MIGRATE 命令被用於執行叢集操作時, target_database 的值總是 0 。
target_database 引數的存在是為了讓 MIGRATE 命令成為一個通用命令, 從而可以作用於叢集以外的其他功能。

我們對 MIGRATE 命令做了優化, 使得它即使在傳輸包含多個元素的列表鍵這樣的複雜資料時, 也可以保持高效。

不過, 儘管 MIGRATE 非常高效, 對一個鍵非常多、並且鍵的資料量非常大的叢集來說, 叢集重配置還是會佔用大量的時間, 可能會導致叢集沒辦法適應那些對於響應時間有嚴格要求的應用程式。

ASK 轉向

在之前介紹 MOVED 轉向的時候, 我們說除了 MOVED 轉向之外, 還有另一種 ASK 轉向。

當節點需要讓一個客戶端長期地(permanently)將針對某個槽的命令請求傳送至另一個節點時, 節點向客戶端返回 MOVED 轉向。

另一方面, 當節點需要讓客戶端僅僅在下一個命令請求中轉向至另一個節點時, 節點向客戶端返回 ASK 轉向。

比如說, 在我們上一節列舉的槽 8 的例子中, 因為槽 8 所包含的各個鍵分散在節點 A 和節點 B 中, 所以當客戶端在節點 A 中沒找到某個鍵時, 它應該轉向到節點 B 中去尋找, 但是這種轉向應該僅僅影響一次命令查詢, 而不是讓客戶端每次都直接去查詢節點 B : 在節點 A 所持有的屬於槽 8 的鍵沒有全部被遷移到節點 B 之前, 客戶端應該先訪問節點 A , 然後再訪問節點 B 。

因為這種轉向只針對 16384 個槽中的其中一個槽, 所以轉向對叢集造成的效能損耗屬於可接受的範圍。

因為上述原因, 如果我們要在查詢節點 A 之後, 繼續查詢節點 B , 那麼客戶端在向節點 B 傳送命令請求之前, 應該先發送一個 ASKING 命令, 否則這個針對帶有 IMPORTING 狀態的槽的命令請求將被節點 B 拒絕執行。

接收到客戶端 ASKING 命令的節點將為客戶端設定一個一次性的標誌(flag), 使得客戶端可以執行一次針對 IMPORTING 狀態的槽的命令請求。

從客戶端的角度來看, ASK 轉向的完整語義(semantics)如下:

如果客戶端接收到 ASK 轉向, 那麼將命令請求的傳送物件調整為轉向所指定的節點。
先發送一個 ASKING 命令,然後再發送真正的命令請求。
不必更新客戶端所記錄的槽 8 至節點的對映: 槽 8 應該仍然對映到節點 A , 而不是節點 B 。

一旦節點 A 針對槽 8 的遷移工作完成, 節點 A 在再次收到針對槽 8 的命令請求時, 就會向客戶端返回 MOVED 轉向, 將關於槽 8 的命令請求長期地轉向到節點 B 。

注意, 即使客戶端出現 Bug , 過早地將槽 8 對映到了節點 B 上面, 但只要這個客戶端不傳送 ASKING 命令, 客戶端傳送命令請求的時候就會遇上 MOVED 錯誤, 並將它轉向回節點 A 。

容錯

節點失效檢測

以下是節點失效檢查的實現方法:

當一個節點向另一個節點發送 PING 命令, 但是目標節點未能在給定的時限內返回 PING 命令的回覆時, 那麼傳送命令的節點會將目標節點標記為 PFAIL (possible failure,可能已失效)。
等待 PING 命令回覆的時限稱為“節點超時時限(node timeout)”, 是一個節點選項(node-wise setting)。
每次當節點對其他節點發送 PING 命令的時候, 它都會隨機地廣播三個它所知道的節點的資訊, 這些資訊裡面的其中一項就是說明節點是否已經被標記為 PFAIL 或者 FAIL 。
當節點接收到其他節點發來的資訊時, 它會記下那些被其他節點標記為失效的節點。 這稱為失效報告(failure report)。
如果節點已經將某個節點標記為 PFAIL , 並且根據節點所收到的失效報告顯式, 叢集中的大部分其他主節點也認為那個節點進入了失效狀態, 那麼節點會將那個失效節點的狀態標記為 FAIL 。
一旦某個節點被標記為 FAIL , 關於這個節點已失效的資訊就會被廣播到整個叢集, 所有接收到這條資訊的節點都會將失效節點標記為 FAIL 。

簡單來說, 一個節點要將另一個節點標記為失效, 必須先詢問其他節點的意見, 並且得到大部分主節點的同意才行。

因為過期的失效報告會被移除, 所以主節點要將某個節點標記為 FAIL 的話, 必須以最近接收到的失效報告作為根據。

在以下兩種情況中, 節點的 FAIL 狀態會被移除:

如果被標記為 FAIL 的是從節點, 那麼當這個節點重新上線時, FAIL 標記就會被移除。
保持(retaning)從節點的 FAIL 狀態是沒有意義的, 因為它不處理任何槽, 一個從節點是否處於 FAIL 狀態, 決定了這個從節點在有需要時能否被提升為主節點。
如果一個主節點被打上 FAIL 標記之後, 經過了節點超時時限的四倍時間, 再加上十秒鐘之後, 針對這個主節點的槽的故障轉移操作仍未完成, 並且這個主節點已經重新上線的話, 那麼移除對這個節點的 FAIL 標記。

在第二種情況中, 如果故障轉移未能順利完成, 並且主節點重新上線, 那麼叢集就繼續使用原來的主節點, 從而免去管理員介入的必要。

叢集狀態檢測(已部分實現)

每當叢集發生配置變化時(可能是雜湊槽更新,也可能是某個節點進入失效狀態), 叢集中的每個節點都會對它所知道的節點進行掃描(scan)。

一旦配置處理完畢, 叢集會進入以下兩種狀態的其中一種:

FAIL : 叢集不能正常工作。 當叢集中有某個節點進入失效狀態時, 叢集不能處理任何命令請求, 對於每個命令請求, 叢集節點都返回錯誤回覆。
OK : 叢集可以正常工作, 負責處理全部 16384 個槽的節點中, 沒有一個節點被標記為 FAIL 狀態。

這說明即使叢集中只有一部分雜湊槽不能正常使用, 整個叢集也會停止處理任何命令。

不過節點從出現問題到被標記為 FAIL 狀態的這段時間裡, 叢集仍然會正常運作, 所以叢集在某些時候, 仍然有可能只能處理針對 16384 個槽的其中一個子集的命令請求。

以下是叢集進入 FAIL 狀態的兩種情況:

至少有一個雜湊槽不可用,因為負責處理這個槽的節點進入了 FAIL 狀態。
叢集中的大部分主節點都進入下線狀態。當大部分主節點都進入 PFAIL 狀態時,叢集也會進入 FAIL 狀態。

第二個檢查是必須的, 因為要將一個節點從 PFAIL 狀態改變為 FAIL 狀態, 必須要有大部分主節點進行投票表決, 但是, 當叢集中的大部分主節點都進入失效狀態時, 單憑一個兩個節點是沒有辦法將一個節點標記為 FAIL 狀態的。

因此, 有了第二個檢查條件, 只要叢集中的大部分主節點進入了下線狀態, 那麼叢集就可以在不請求這些主節點的意見下, 將某個節點判斷為 FAIL 狀態, 從而讓整個叢集停止處理命令請求。

從節點選舉

一旦某個主節點進入 FAIL 狀態, 如果這個主節點有一個或多個從節點存在, 那麼其中一個從節點會被升級為新的主節點, 而其他從節點則會開始對這個新的主節點進行復制。

新的主節點由已下線主節點屬下的所有從節點中自行選舉產生, 以下是選舉的條件:

這個節點是已下線主節點的從節點。
已下線主節點負責處理的槽數量非空。
從節點的資料被認為是可靠的, 也即是, 主從節點之間的複製連線(replication link)的斷線時長不能超過節點超時時限(node timeout)乘以 REDIS_CLUSTER_SLAVE_VALIDITY_MULT 常量得出的積。

如果一個從節點滿足了以上的所有條件, 那麼這個從節點將向叢集中的其他主節點發送授權請求, 詢問它們, 是否允許自己(從節點)升級為新的主節點。

如果傳送授權請求的從節點滿足以下屬性, 那麼主節點將向從節點返回 FAILOVER_AUTH_GRANTED 授權, 同意從節點的

升級要求:

傳送授權請求的是一個從節點, 並且它所屬的主節點處於 FAIL 狀態。

在已下線主節點的所有從節點中, 這個從節點的節點 ID 在排序中是最小的。

這個從節點處於正常的執行狀態: 它沒有被標記為 FAIL 狀態, 也沒有被標記為 PFAIL 狀態。

一旦某個從節點在給定的時限內得到大部分主節點的授權, 它就會開始執行以下故障轉移操作:

通過 PONG 資料包(packet)告知其他節點, 這個節點現在是主節點了。

通過 PONG 資料包告知其他節點, 這個節點是一個已升級的從節點(promoted slave)。

接管(claiming)所有由已下線主節點負責處理的雜湊槽。

顯式地向所有節點廣播一個 PONG 資料包, 加速其他節點識別這個節點的進度, 而不是等待定時的 PING / PONG 資料包。

所有其他節點都會根據新的主節點對配置進行相應的更新,特別地:

所有被新的主節點接管的槽會被更新。

已下線主節點的所有從節點會察覺到 PROMOTED 標誌, 並開始對新的主節點進行復制。

如果已下線的主節點重新回到上線狀態, 那麼它會察覺到 PROMOTED 標誌, 並將自身調整為現任主節點的從節點。

在叢集的生命週期中, 如果一個帶有 PROMOTED 標識的主節點因為某些原因轉變成了從節點, 那麼該節點將丟失它所帶有的 PROMOTED 標識。

釋出/訂閱(已實現,但仍然需要改善)

在一個 Redis 叢集中, 客戶端可以訂閱任意一個節點, 也可以向任意一個節點發送資訊, 節點會對客戶端所傳送的資訊進行轉發。

在目前的實現中, 節點會將接收到的資訊廣播至叢集中的其他所有節點, 在將來的實現中, 可能會使用 bloom filter 或者其他演算法來優化這一操作。

附錄 A: CRC16 演算法的 ANSI 實現參考

/*
 * Copyright 2001-2010 Georges Menie (www.menie.org)
 * Copyright 2010 Salvatore Sanfilippo (adapted to Redis coding style)
 * All rights reserved.
 * Redistribution and use in source and binary forms,with or without
 * modification,are permitted provided that the following conditions are met:
 *
 *   * Redistributions of source code must retain the above copyright
 *    notice,this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *    notice,this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *   * Neither the name of the University of California,Berkeley nor the
 *    names of its contributors may be used to endorse or promote products
 *    derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES,INCLUDING,BUT NOT LIMITED TO,THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS AND CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT,INDIRECT,INCIDENTAL,SPECIAL,EXEMPLARY,OR CONSEQUENTIAL DAMAGES
 * (INCLUDING,PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE,DATA,OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY,WHETHER IN CONTRACT,STRICT LIABILITY,OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE,EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
/* CRC16 implementation acording to CCITT standards.
 *
 * Note by @antirez: this is actually the XMODEM CRC 16 algorithm,using the
 * following parameters:
 *
 * Name            : "XMODEM",also known as "ZMODEM","CRC-16/ACORN"
 * Width           : 16 bit
 * Poly            : 1021 (That is actually x^16 + x^12 + x^5 + 1)
 * Initialization       : 0000
 * Reflect Input byte     : False
 * Reflect Output CRC     : False
 * Xor constant to output CRC : 0000
 * Output for "123456789"   : 31C3
 */
static const uint16_t crc16tab[256]= {
  0x0000,0x1021,0x2042,0x3063,0x4084,0x50a5,0x60c6,0x70e7,0x8108,0x9129,0xa14a,0xb16b,0xc18c,0xd1ad,0xe1ce,0xf1ef,0x1231,0x0210,0x3273,0x2252,0x52b5,0x4294,0x72f7,0x62d6,0x9339,0x8318,0xb37b,0xa35a,0xd3bd,0xc39c,0xf3ff,0xe3de,0x2462,0x3443,0x0420,0x1401,0x64e6,0x74c7,0x44a4,0x5485,0xa56a,0xb54b,0x8528,0x9509,0xe5ee,0xf5cf,0xc5ac,0xd58d,0x3653,0x2672,0x1611,0x0630,0x76d7,0x66f6,0x5695,0x46b4,0xb75b,0xa77a,0x9719,0x8738,0xf7df,0xe7fe,0xd79d,0xc7bc,0x48c4,0x58e5,0x6886,0x78a7,0x0840,0x1861,0x2802,0x3823,0xc9cc,0xd9ed,0xe98e,0xf9af,0x8948,0x9969,0xa90a,0xb92b,0x5af5,0x4ad4,0x7ab7,0x6a96,0x1a71,0x0a50,0x3a33,0x2a12,0xdbfd,0xcbdc,0xfbbf,0xeb9e,0x9b79,0x8b58,0xbb3b,0xab1a,0x6ca6,0x7c87,0x4ce4,0x5cc5,0x2c22,0x3c03,0x0c60,0x1c41,0xedae,0xfd8f,0xcdec,0xddcd,0xad2a,0xbd0b,0x8d68,0x9d49,0x7e97,0x6eb6,0x5ed5,0x4ef4,0x3e13,0x2e32,0x1e51,0x0e70,0xff9f,0xefbe,0xdfdd,0xcffc,0xbf1b,0xaf3a,0x9f59,0x8f78,0x9188,0x81a9,0xb1ca,0xa1eb,0xd10c,0xc12d,0xf14e,0xe16f,0x1080,0x00a1,0x30c2,0x20e3,0x5004,0x4025,0x7046,0x6067,0x83b9,0x9398,0xa3fb,0xb3da,0xc33d,0xd31c,0xe37f,0xf35e,0x02b1,0x1290,0x22f3,0x32d2,0x4235,0x5214,0x6277,0x7256,0xb5ea,0xa5cb,0x95a8,0x8589,0xf56e,0xe54f,0xd52c,0xc50d,0x34e2,0x24c3,0x14a0,0x0481,0x7466,0x6447,0x5424,0x4405,0xa7db,0xb7fa,0x8799,0x97b8,0xe75f,0xf77e,0xc71d,0xd73c,0x26d3,0x36f2,0x0691,0x16b0,0x6657,0x7676,0x4615,0x5634,0xd94c,0xc96d,0xf90e,0xe92f,0x99c8,0x89e9,0xb98a,0xa9ab,0x5844,0x4865,0x7806,0x6827,0x18c0,0x08e1,0x3882,0x28a3,0xcb7d,0xdb5c,0xeb3f,0xfb1e,0x8bf9,0x9bd8,0xabbb,0xbb9a,0x4a75,0x5a54,0x6a37,0x7a16,0x0af1,0x1ad0,0x2ab3,0x3a92,0xfd2e,0xed0f,0xdd6c,0xcd4d,0xbdaa,0xad8b,0x9de8,0x8dc9,0x7c26,0x6c07,0x5c64,0x4c45,0x3ca2,0x2c83,0x1ce0,0x0cc1,0xef1f,0xff3e,0xcf5d,0xdf7c,0xaf9b,0xbfba,0x8fd9,0x9ff8,0x6e17,0x7e36,0x4e55,0x5e74,0x2e93,0x3eb2,0x0ed1,0x1ef0
};
uint16_t crc16(const char *buf,int len) {
  int counter;
  uint16_t crc = 0;
  for (counter = 0; counter < len; counter++)
      crc = (crc<<8) ^ crc16tab[((crc>>8) ^ *buf++)&0x00FF];
  return crc;
}

總結

以上就是本文關於redis叢集規範詳解的全部內容,希望對大家有所幫助。感興趣的朋友可以參閱:詳細分析Redis叢集故障、Redis原始碼解析:叢集手動故障轉移、從節點遷移詳解、簡述Redis和MySQL的區別等,如有不足之處,請留言指出,小編悉心更正。感謝朋友們對我們網站的支援!