Redis 集羣規範

什麼是 Redis 集羣??
Redis 集羣是一個分佈式(distributed)、容錯(fault-tolerant)的 Redis 實現,集羣可使用的功能是普通單機 Redis 所能使用的功能的一個子集(subset)。node

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

Redis 集羣爲了保證一致性(consistency)而犧牲了一部分容錯性:系統會在保證對網絡斷線(net split)和節點失效(node failure)具備有限(limited)抵抗力的前提下,儘量地保持數據的一致性。算法

Note數據庫

集羣將節點失效視爲網絡斷線的其中一種特殊狀況。緩存

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

主節點和從節點使用徹底相同的服務器實現,它們的功能(functionally)也徹底同樣,但從節點一般僅用於替換失效的主節點。
不過,若是不須要保證「先寫入,後讀取」操做的一致性(read-after-write consistency),那麼可使用從節點來執行只讀查詢。
Redis 集羣實現的功能子集?
Redis 集羣實現了單機 Redis 中,全部處理單個數據庫鍵的命令。服務器

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

在未來,用戶也許能夠經過 MIGRATE COPY 命令,在集羣的計算節點(computation node)中執行鍼對多個數據庫鍵的只讀操做,但集羣自己不會去實現那些須要將多個數據庫鍵在多個節點中移來移去的複雜多鍵命令。架構

Redis 集羣不像單機 Redis 那樣支持多數據庫功能,集羣只使用默認的 0 號數據庫,而且不能使用 SELECT 命令。app

Redis 集羣協議中的客戶端和服務器?
Redis 集羣中的節點有如下責任:

持有鍵值對數據。
記錄集羣的狀態,包括鍵到正確節點的映射(mapping keys to right nodes)。
自動發現其餘節點,識別工做不正常的節點,並在有須要時,在從節點中選舉出新的主節點。
爲了執行以上列出的任務,集羣中的每一個節點都與其餘節點創建起了「集羣鏈接(cluster bus)」,該鏈接是一個 TCP 鏈接,使用二進制協議進行通信。

節點之間使用 Gossip 協議 來進行如下工做:

傳播(propagate)關於集羣的信息,以此來發現新的節點。
向其餘節點發送 PING 數據包,以此來檢查目標節點是否正常運做。
在特定事件發生時,發送集羣信息。
除此以外,集羣鏈接還用於在集羣中發佈或訂閱信息。

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

由於客戶端能夠自由地向集羣中的任何一個節點發送命令請求,並能夠在有須要時,根據轉向錯誤所提供的信息,將命令轉發至正確的節點,因此在理論上來講,客戶端是無須保存集羣狀態信息的。

不過,若是客戶端能夠將鍵和節點之間的映射信息保存起來,能夠有效地減小可能出現的轉向次數,籍此提高命令執行的效率。

鍵分佈模型?
Redis 集羣的鍵空間被分割爲 16384 個槽(slot),集羣的最大節點數量也是 16384 個。

Note

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

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

當咱們說一個集羣處於「穩定」(stable)狀態時,指的是集羣沒有在執行重配置(reconfiguration)操做,每一個哈希槽都只由一個節點進行處理。

Note

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

Note

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

如下是負責將鍵映射到槽的算法:

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, BUT NOT LIMITED TO, 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 Cluster 功能特性
Redis 集羣是分佈式的redis 實現,具備如下特性:

1. 高可用性與可線性擴張到1000個節點
2. 數據自動路由到多個節點
3. 節點間數據共享
4. 可動態添加或者刪除節點
5. 部分節點不可達時,集羣仍可用
6. 數據經過異步複製,不保證數據的強一致性
7. 可動態調整數據分佈
 

Redis 集羣架構圖


其中
一: Redis 集羣協議
一、Redis 集羣,節點負責存儲數據、記錄集羣狀態,集羣節點能自動發現其餘節點,檢測出節點的狀態,並在須要的時候,推選中主節點
二、Redis 集羣節點中經過TCP鏈接和一個二級制協議(cluster bus) 創建通訊。發現新的節點、發送PING包、特定的狀況下發送集羣消息。集羣鏈接可以發佈與訂閱消息
三、Redis 集羣節點不能代理請求,客戶端發起請求後,接收到重定向(MOVED\ASK)錯誤,會自動重定向到其餘節點。理論上來講,客戶端是能夠自由地向集羣中的全部節點發送請求,在須要的時候把請求重定向到其餘節點,因此客戶端是不須要保存集羣狀態。 不過客戶端能夠緩存鍵值和節點之間的映射關係,這樣能明顯提升命令執行的效率。
二: 安全寫入
Redis 集羣節點之間使用異步複製,在分區過程當中存在窗口,容易致使丟失寫入數據,Redis集羣即便努力嘗試全部寫入,可是如下兩種狀況可能丟失數據:
一、命令操做到達主節點後,但在主節點回復的時候,此時寫入可能尚未經過主節點複製到從節點那裏。若是這時候主庫宕機了,這條命令永久丟失。以防主節點長時間不可達而它的一個從節點已經被提高爲主節點。
二、分區致使一個主節點不可達,然而集羣發送故障轉移(failover),提高從節點爲主節點,原來的主節點再次恢復。一個沒有更新路由表(routing table)的客戶端或許會在集羣把這個主節點變成一個從節點(新主節點的從節點)以前對它進行寫入操做。致使數據完全丟失
三: 可用性
Redis 集羣少數節點不可用後,在通過cluster-node-timeout時間後,集羣根據自動故障機制,將從節點提高爲主節點。這事集羣恢復可用
舉個例子,一個由 N 個主節點組成的集羣,每一個主節點都只有一個從節點。當有一個節點(由於故障)被分割出去後,集羣的多數節點這邊仍然是可訪問的。當有兩個節點(因故障)被分割出去後集羣仍可用的機率是 1-(1/(N*2-1))(在第一個節點故障出錯後總共剩下 N*2-1 個節點,那麼失去冗餘備份(即失去從節點)的那個主節點也故障出錯的機率是 1/(N*2-1)))。
好比一個擁有6個節點的集羣,每一個節點都只有一個從節點,那麼在兩個節點從多數節點這邊分割出去後集羣再也不可用的機率是 1/(6*2-1) = 0.0909,即有大約 9% 的機率。
Redis 集羣數據分佈
Redis 集羣沒有使用一致性hash,引入了哈希槽(HASH SLOT).
Redis 集羣中全部的主節點都負責 16384 個哈希槽中的一部分。當集羣處於穩定狀態時,集羣中沒有在執行重配置(reconfiguration)操做,每一個哈希槽都只由一個節點進行處理(不過主節點能夠有一個或多個從節點,能夠在網絡斷線或節點失效時替換掉主節點)
slot = CRC16(KEY) / 16384
Redis 集羣鍵HASH標籤

目標:
   HASH 標籤是確保兩個KEY 都能在同一個HASH槽的一種方式。
實現方式:
   HASH 槽是用另外一種不一樣的計算方式計算的。基原本說,若是KEY包含一個"{...}"這樣的模式,只有「{」 和 「}」 之間的字符串會被用來作HASH以獲取HAS槽。若是同時出現多個「{}」 計算方式以下:
   * 若是KEY 包含一個 「{」 字符
   * 那麼在 「{」的右邊就會字符 "}"
   * 在字符 "{" 和 "}"直接會有一個或多個字符。可是第一個"}" 必定會出如今第一個"{"以後
   * 只有在第一個 { 和它右邊第一個 } 之間的內容會被用來計算哈希值
例子:
   一、好比這兩個鍵 user:{1000}.following 和user:{1000}.followers 會被哈希到同一個哈希槽裏,由於只有 "1000" 這個子串會被用來計算哈希值。
   二、對於 user{}{list} 這個鍵,整個鍵都會被用來計算哈希值,由於第一個出現的 { 和它右邊第一個出現的 } 之間沒有任何字符。
   三、對於 user{{momoid}}following 這個鍵,用來計算哈希值的是 "{momoid" 這個子串,由於它是第一個 { 及其右邊第一個 } 之間的內容。
   四、對於 user{momoid}{following} 這個鍵,用來計算哈希值的是 "momoid" 這個子串,由於算法會在第一次有效或無效(好比中間沒有任何字節)地匹配到 { 和 } 的時候中止。
   五、按照這個算法,若是一個鍵是以 {} 開頭的話,那麼就看成整個鍵會被用來計算哈希值。當使用二進制數據作爲鍵名稱的時候,這是很是有用的。
Redis 集羣相關命令
集羣
   一、CLUSTER INFO 打印集羣的信息  
   二、CLUSTER NODES 列出集羣當前已知的全部節點(node),以及這些節點的相關信息。
   三、CLUSTER FAILOVER 手動故障轉移,須要在轉移的主節點的從節點上執行
節點  
   一、CLUSTER MEET 將 ip 和 port 所指定的節點添加到集羣當中,讓它成爲集羣的一份子。  
   二、CLUSTER FORGET 從集羣中移除 node_id 指定的節點。  
   三、CLUSTER REPLICATE 將當前節點設置爲 node_id 指定的節點的從節點。  
   四、CLUSTER SAVECONFIG 將節點的配置文件保存到硬盤裏面。  
槽(slot)  
   一、CLUSTER ADDSLOTS [slot ...] 將一個或多個槽(slot)指派(assign)給當前節點。  
   二、CLUSTER DELSLOTS [slot ...] 移除一個或多個槽對當前節點的指派。  
   三、CLUSTER FLUSHSLOTS 移除指派給當前節點的全部槽,讓當前節點變成一個沒有指派任何槽的節點。  
   四、CLUSTER SETSLOT NODE 將槽 slot 指派給 node_id 指定的節點,若是槽已經指派給另外一個節點,那麼先讓另外一個節點刪除該槽,而後再進行指派。  
   一、CLUSTER SETSLOT MIGRATING 將本節點的槽 slot 遷移到 node_id 指定的節點中。  
   二、CLUSTER SETSLOT IMPORTING 從 node_id 指定的節點中導入槽 slot 到本節點。  
   三、CLUSTER SETSLOT STABLE 取消對槽 slot 的導入(import)或者遷移(migrate)。  
鍵  
   一、CLUSTER KEYSLOT 計算鍵 key 應該被放置在哪一個槽上。  
   二、CLUSTER COUNTKEYSINSLOT 返回槽 slot 目前包含的鍵值對數量。  
   三、CLUSTER GETKEYSINSLOT 返回 count 個 slot 槽中的鍵。    
不支持的命令:
   一、不支持SELECT 命令,集羣只使用數據庫 0
   二、不支持多個KEY的操做 如 MSET、SUION、SINTER等命令 (由於KEYS 沒法hash到同一個slot中)

redis-trib.rb 相關命令
   一、redis-trib.rb create [--replicas N] host:ip [host:ip ...]  建立集羣
   二、redis-trib.rb add-node host:ip host:ip  將前面的host:ip 添加到集羣中
   三、redis-trib.rb check host:ip 檢查集羣的狀態
   四、redis-trib.rb reshard host:ip OR  redis-trib.rb reshard --from host:port --to host:port --slots --yes集羣從新分片
   五、redis-trib.rb del-node host:ip 'NODE ID' 將節點從集羣中移除
Redis 集羣配置
redis 集羣須要運行在 集羣模式的redis實例,不是普通的redis實例。集羣模式須要添加集羣相關的配置。開啓集羣模式的redis實例,可使用集羣特有的命令和特性

其中相關配置以下:

必須配置:
cluster-enabled yes                         --> 開啓集羣模式
cluster-config-file nodes-30000.conf        --> 集羣相關的信息
cluster-node-timeout 15000                  --> 節點超時時間,用來failover的操做

可選配置:
cluster-slave-validity-factor 10            
cluster-migration-barrier 1
cluster-require-full-coverage yes
Redis Cluster 主從搭建
啓動集羣模式的實例(與普通啓動方式一致),不需搭建主從關係

搭建集羣: 在上述啓動的6個redis實例中,搭建集羣。經過redis自帶的集羣命令行工具 redis-trib.rb 。 redis-trib.rb 位於redis源碼包中src文件中。它能夠完成建立集羣、檢查集羣、集羣reshard、添加節點、刪除節點等操做

建立集羣 redis-trib.rb create [–replicas [N]] host:ip [host:ip]
redis-trib.rb create --replicas 1 127.0.0.1:30000 127.0.0.1:30001 127.0.0.1:30001 127.0.0.1:31000 127.0.0.1:31001 127.0.0.1:31002
其中 --replicas N 選項代表集羣中的每一個節點帶幾個從節點

輸出日誌:
   [OK] All 16384 slots covered
集羣狀態檢查 redis-trib.rb check host:ip
redis-trib.rb check 127.0.0.1:30000
輸出日誌:
   Connecting to node 127.0.0.1:30000: OK
   Connecting to node 127.0.0.1:30002: OK
   Connecting to node 127.0.0.1:31000: OK
   Connecting to node 127.0.0.1:31001: OK
   Connecting to node 127.0.0.1:30001: OK
   Connecting to node 127.0.0.1:31002: OK
   >>> Performing Cluster Check (using node 127.0.0.1:30000)
   M: 36801ef9849f12526be1e954f9e6f6fa24c50d46 127.0.0.1:30000
       slots:0-5961,10923-11421 (6461 slots) master
       1 additional replica(s)
   M: 98c4c66ee189569dec47a9600b057f90626cc6a7 127.0.0.1:30002
       slots:11422-16383 (4962 slots) master
       1 additional replica(s)
   S: 54d7d1241b1d9c24f76d99e9814d8cf8d8db474e 127.0.0.1:31000
       slots: (0 slots) slave
       replicates 36801ef9849f12526be1e954f9e6f6fa24c50d46
   S: 3f5ae989b9b1b6617c53e77ed4853b618408bbe6 127.0.0.1:31001
       slots: (0 slots) slave
       replicates 6b880ae14f8c9dbfd54f8c4811cf0c039d523216
   M: 6b880ae14f8c9dbfd54f8c4811cf0c039d523216 127.0.0.1:30001
       slots:5962-10922 (4961 slots) master
       1 additional replica(s)
   S: 81a3a70ce2fbb8bcce2e9be9ed77e34d9d4d5b21 127.0.0.1:31002
       slots: (0 slots) slave
       replicates 98c4c66ee189569dec47a9600b057f90626cc6a7
   [OK] All nodes agree about slots configuration.
   >>> Check for open slots...
   >>> Check slots coverage...
   [OK] All 16384 slots covered.
添加節點: 啓動一個新的集羣模式的redis實例。使用 redis-trib.rb add-node host:ip host:ip
redis-trib.rb add-node 127.0.0.1:30004 127.0.0.1:3000
其中  127.0.0.1:30004 爲新節點  127.0.0.1:30000 爲集羣中任意節點

查看集羣節點:
   redis-cli  -c -p 30000
   127.0.0.1:30000> CLUSTER NODES
   98c4c66ee189569dec47a9600b057f90626cc6a7 127.0.0.1:30002 master - 0 1429686483614 3 connected 11422-16383
   54d7d1241b1d9c24f76d99e9814d8cf8d8db474e 127.0.0.1:31000 slave 36801ef9849f12526be1e954f9e6f6fa24c50d46 0 1429686485615 7 connected
   3f5ae989b9b1b6617c53e77ed4853b618408bbe6 127.0.0.1:31001 slave 6b880ae14f8c9dbfd54f8c4811cf0c039d523216 0 1429686484614 5 connected
   2eb135bf03dbdbc57e704578b2833cc3fb860b6e 127.0.0.1:30004 master - 0 1429686479607 0 connected   --> 新節點
   6b880ae14f8c9dbfd54f8c4811cf0c039d523216 127.0.0.1:30001 master - 0 1429686481612 2 connected 5962-10922
   81a3a70ce2fbb8bcce2e9be9ed77e34d9d4d5b21 127.0.0.1:31002 slave 98c4c66ee189569dec47a9600b057f90626cc6a7 0 1429686482613 6 connected
   36801ef9849f12526be1e954f9e6f6fa24c50d46 127.0.0.1:30000 myself,master - 0 0 7 connected 0-5961 10923-11421

輸出信息解析:
   一、節點ID
   二、IP:PORT
   三、節點狀態標識: master、slave、myself、fail?、fail
   四、若是是從節點,表示主節點的ID。若是是主節點,爲 '-'
   五、集羣最近一次向各個節點發送PING命令後,過去多長時間尚未接到回覆
   六、節點最近一次返回PONG的時間戳
   七、節點的配置紀元
   八、本節點的網絡鏈接狀況: connected、disconnected
   九、若是是主節點,表示節點包含的曹
添加從節點: CLUSTER REPLICATE ID

127.0.0.1:31004> CLUSTER REPLICATE 2eb135bf03dbdbc57e704578b2833cc3fb860b6e
其中  2eb135bf03dbdbc57e704578b2833cc3fb860b6e 爲主庫的集羣ID
集羣reshard: 爲新節點分片slots redis-trib.rb reshard host:port
redis-trib.rb reshard 127.0.0.1:30004
日誌輸出:
Shell# redis-trib.rb reshard 127.0.0.1:30004
Connecting to node 127.0.0.1:30004: OK
Connecting to node 127.0.0.1:30000: OK
Connecting to node 127.0.0.1:31001: OK
Connecting to node 127.0.0.1:30001: OK
Connecting to node 127.0.0.1:31000: OK
Connecting to node 127.0.0.1:30002: OK
Connecting to node 127.0.0.1:31002: OK
>>> Performing Cluster Check (using node 127.0.0.1:30004)
M: 2eb135bf03dbdbc57e704578b2833cc3fb860b6e 127.0.0.1:30004      --> 新節點信息
  slots: (0 slots) master
  0 additional replica(s)
M: 36801ef9849f12526be1e954f9e6f6fa24c50d46 127.0.0.1:30000
  slots:0-5961,10923-11421 (6461 slots) master
  1 additional replica(s)
S: 3f5ae989b9b1b6617c53e77ed4853b618408bbe6 127.0.0.1:31001
  slots: (0 slots) slave
  replicates 6b880ae14f8c9dbfd54f8c4811cf0c039d523216
M: 6b880ae14f8c9dbfd54f8c4811cf0c039d523216 127.0.0.1:30001
  slots:5962-10922 (4961 slots) master
  1 additional replica(s)
S: 54d7d1241b1d9c24f76d99e9814d8cf8d8db474e 127.0.0.1:31000
  slots: (0 slots) slave
  replicates 36801ef9849f12526be1e954f9e6f6fa24c50d46
M: 98c4c66ee189569dec47a9600b057f90626cc6a7 127.0.0.1:30002
  slots:11422-16383 (4962 slots) master
  1 additional replica(s)
S: 81a3a70ce2fbb8bcce2e9be9ed77e34d9d4d5b21 127.0.0.1:31002
  slots: (0 slots) slave
  replicates 98c4c66ee189569dec47a9600b057f90626cc6a7
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 1000               --> slot數量
What is the receiving node ID? 2eb135bf03dbdbc57e704578b2833cc3fb860b6e  --> 接收集羣NODE ID  
Please enter all the source node IDs.
 Type 'all' to use all the nodes as source nodes for the hash slots.
 Type 'done' once you entered all the source nodes IDs.
Source node #1:36801ef9849f12526be1e954f9e6f6fa24c50d46                  --> 來源NODE ID
Source node #2:done
......
Redis Cluster failover機制
節點心跳
Redis 集羣在運行的過程當中,每一個節點每秒會隨機ping幾個節點,不過每一個節點都會保證去PING知足這個條件的其餘節點:在過去的一半"cluster-node-timeout"時間裏都沒有發送PING包過去或者沒有接收從那節點發來的PONG包的節點。在"cluster-node-timeout"時間過去以前,若TCP鏈接有問題,節點會嘗試去重試鏈接,確保本身不會被當作不可達的節點。
若是 「cluster-node-time」 被設爲一個很小的數而節點數量(N)很是大,那麼消息交流數量會比 O(N) 更大,由於每一個節點都會嘗試去 ping 每個在過去一半 NODE_TIMEOUT 時間裏都沒更新信息的節點。
失效檢測
Redis 集羣失效檢測是用來識別出大多數節點什麼時候沒法訪問某一個主節點或從節點。當這個事件發生時,就提高一個從節點來作主節點;若若是沒法提高從節點來作主節點的話,那麼整個集羣就置爲錯誤狀態並中止接收客戶端的查詢
每一個節點都有一份跟其餘已知節點相關的標識列表。其中有兩個標識是用於失效檢測,分別是 PFAIL 和 FAIL.
PFAIL 標識: 表示可能失效(Possible failure),這是一個非公認的(non acknowledged)失效類型。
當一個節點在超過 "cluster-node-timeout" 時間後仍沒法訪問某個節點,那麼它會用 PFAIL 來標識這個不可達的節點。不管節點類型是什麼,主節點和從節點都能標識其餘的節點爲 PFAIL
FAIL 表示一個節點已經失效,並且這個狀況已經被大多數主節點在某段固定時間內確認過的了。
知足如下條件,PFAIL 狀態升級爲 FAIL 狀態:(設定集羣含有 A B C AS BS CS 六個節點)
一、節點A正常,節點C 狀態爲 PFAIL
二、節點A 經過gossip字段收集集羣中大部分節點標識節點C的狀態信息
三、若是大部分節點標識節點C爲 PFAIL 狀態,或者 cluster-node-timeout *  FAIL_REPORT_VALIDITY_MULT 這段時間內處於 PFAIL狀態

此時節點A 會標記 節點C 爲 FAIL 狀態,並向全部的節點發送關於節點C的 FAIL 信息。 FAIL 信息會強制接收的節點把節點C 標識爲 FAIL 狀態

NOTE:
FAIL 標識基本都是單向的,也就是說,一個節點能從 PFAIL 狀態升級到 FAIL 狀態. 清除FAIL狀態的方法:
一、節點已經恢復可達的,而且它是一個從節點。在這種狀況下,FAIL 標識能夠清除掉,由於從節點並無被故障轉移。
二、節點已經恢復可達的,並且它是一個主節點,但通過了很長時間(N * NODE_TIMEOUT)後也沒有檢測到任何從節點被提高了。
從選舉與提高
從節點的選舉與提高都是由從節點處理的,主節點會投票要提高哪一個從節點。當知足如下條件,一個節點能夠發起選舉:
一、該從節點的主節點處理 FALI 狀態
二、這個主節點負責的HASH曹個數不爲O
三、從節點和主節點之間的重複鏈接(replication link)斷線不超過一段給定的時間,這是爲了確保從節點的數據是可靠
一旦從節點被推選出來,就會想主節點請求投票,一旦從節點贏得投票,它會響全部其餘節點發送PING 和 PONG 數據包,宣佈本身已經成爲主節點,而且提供它的HASH槽信息,並配置 currentEpoch 信息
爲了加速其餘節點的從新配置,該節點會廣播一個 pong 包 給集羣裏的全部節點(那些如今訪問不到的節點最終也會收到一個 ping 包或 pong 包,而且進行從新配置)。其餘節點會檢測到有一個新的主節點(帶着更大的configEpoch)在負責處理以前一箇舊的主節點負責的哈希槽,而後就升級本身的配置信息。 舊主節點的從節點,或者是通過故障轉移後從新加入集羣的該舊主節點,不只會升級配置信息,還會配置新主節點的備份。
模擬宕機(實現故障轉移)
集羣當期轉態:

127.0.0.1:30000> CLUSTER NODES
98c4c66ee189569dec47a9600b057f90626cc6a7 127.0.0.1:30002 master - 0 1429696352375 3 connected 11422-16383
54d7d1241b1d9c24f76d99e9814d8cf8d8db474e 127.0.0.1:31000 slave 36801ef9849f12526be1e954f9e6f6fa24c50d46 0 1429696349869 9 connected
3f5ae989b9b1b6617c53e77ed4853b618408bbe6 127.0.0.1:31001 slave 6b880ae14f8c9dbfd54f8c4811cf0c039d523216 0 1429696346364 5 connected
6b880ae14f8c9dbfd54f8c4811cf0c039d523216 127.0.0.1:30001 master - 0 1429696351373 2 connected 5962-10922
81a3a70ce2fbb8bcce2e9be9ed77e34d9d4d5b21 127.0.0.1:31002 slave 98c4c66ee189569dec47a9600b057f90626cc6a7 0 1429696350370 6 connected
36801ef9849f12526be1e954f9e6f6fa24c50d46 127.0.0.1:30000 myself,master - 0 0 9 connected 0-5961 10923-11421

其中 127.0.0.1:31000 節點爲 127.0.0.1:30000 從節點
關閉其中的一個節點後(127.0.0.1:30000)的集羣狀態:

127.0.0.1:30001> CLUSTER NODES
81a3a70ce2fbb8bcce2e9be9ed77e34d9d4d5b21 127.0.0.1:31002 slave 98c4c66ee189569dec47a9600b057f90626cc6a7 0 1429696448146 6 connected
98c4c66ee189569dec47a9600b057f90626cc6a7 127.0.0.1:30002 master - 0 1429696447143 3 connected 11422-16383
6b880ae14f8c9dbfd54f8c4811cf0c039d523216 127.0.0.1:30001 myself,master - 0 0 2 connected 5962-10922
36801ef9849f12526be1e954f9e6f6fa24c50d46 127.0.0.1:30000 master,fail? - 1429696434521 1429696430116 9 disconnected 0-5961 10923-11421
3f5ae989b9b1b6617c53e77ed4853b618408bbe6 127.0.0.1:31001 slave 6b880ae14f8c9dbfd54f8c4811cf0c039d523216 0 1429696445139 5 connected
54d7d1241b1d9c24f76d99e9814d8cf8d8db474e 127.0.0.1:31000 slave 36801ef9849f12526be1e954f9e6f6fa24c50d46 0 1429696449148 9 connected

其中 127.0.0.1:30000 的狀態fail? 表示正在判斷是否失敗

127.0.0.1:30001> CLUSTER NODES
81a3a70ce2fbb8bcce2e9be9ed77e34d9d4d5b21 127.0.0.1:31002 slave 98c4c66ee189569dec47a9600b057f90626cc6a7 0 1429696473317 6 connected
98c4c66ee189569dec47a9600b057f90626cc6a7 127.0.0.1:30002 master - 0 1429696474317 3 connected 11422-16383
6b880ae14f8c9dbfd54f8c4811cf0c039d523216 127.0.0.1:30001 myself,master - 0 0 2 connected 5962-10922
36801ef9849f12526be1e954f9e6f6fa24c50d46 127.0.0.1:30000 master,fail - 1429696434521 1429696430116 9 disconnected
3f5ae989b9b1b6617c53e77ed4853b618408bbe6 127.0.0.1:31001 slave 6b880ae14f8c9dbfd54f8c4811cf0c039d523216 0 1429696472315 5 connected
54d7d1241b1d9c24f76d99e9814d8cf8d8db474e 127.0.0.1:31000 master - 0 1429696471313 10 connected 0-5961 10923-11421

其中 127.0.0.1:30000 的狀態 fail 表示節點失敗,127.0.0.1:30000 節點提高爲主庫。
恢復關閉的實例

127.0.0.1:30001> CLUSTER NODES
81a3a70ce2fbb8bcce2e9be9ed77e34d9d4d5b21 127.0.0.1:31002 slave 98c4c66ee189569dec47a9600b057f90626cc6a7 0 1429696545465 6 connected
98c4c66ee189569dec47a9600b057f90626cc6a7 127.0.0.1:30002 master - 0 1429696542960 3 connected 11422-16383
6b880ae14f8c9dbfd54f8c4811cf0c039d523216 127.0.0.1:30001 myself,master - 0 0 2 connected 5962-10922
36801ef9849f12526be1e954f9e6f6fa24c50d46 127.0.0.1:30000 slave 54d7d1241b1d9c24f76d99e9814d8cf8d8db474e 0 1429696542458 10 connected
3f5ae989b9b1b6617c53e77ed4853b618408bbe6 127.0.0.1:31001 slave 6b880ae14f8c9dbfd54f8c4811cf0c039d523216 0 1429696546467 5 connected
54d7d1241b1d9c24f76d99e9814d8cf8d8db474e 127.0.0.1:31000 master - 0 1429696547470 10 connected 0-5961 10923-11421

其中 127.0.0.1:30000 變成 127.0.0.1:31000的從庫
總結:

優勢:    一、redis 在主節點下線後,從節點會自動提高爲主節點,提供服務    二、redis 宕機節點恢復後,自動會添加到集羣中,變成從節點缺點:    一、因爲redis的複製使用異步機制,在自動故障轉移的過程當中,集羣可能會丟失寫命令。然而 redis 幾乎是同時執行(將命令恢復發送給客戶端,以及將命令複製到從節點)這兩個操做,因此實際中,命令丟失的窗口很是小。--------------------- 做者:An_angel_of_joy 來源:CSDN 原文:https://blog.csdn.net/u010258235/article/details/50060127 版權聲明:本文爲博主原創文章,轉載請附上博文連接!

相關文章
相關標籤/搜索