溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

redis集群規(guī)范詳解

發(fā)布時(shí)間:2020-10-25 01:50:20 來源:腳本之家 閱讀:145 作者:mengwei 欄目:數(shù)據(jù)庫

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

引言

這個(gè)文檔是正在開發(fā)中的 Redis 集群功能的規(guī)范(specification)文檔, 文檔分為兩個(gè)部分:

第一部分介紹目前已經(jīng)在 unstable 分支中實(shí)現(xiàn)了的那些功能。
第二部分介紹目前仍未實(shí)現(xiàn)的那些功能。

文檔各個(gè)部分的內(nèi)容可能會(huì)隨著集群功能的設(shè)計(jì)修改而發(fā)生改變, 其中, 未實(shí)現(xiàn)功能發(fā)生修改的幾率比已實(shí)現(xiàn)功能發(fā)生修改的幾率要高。

這個(gè)規(guī)范包含了編寫客戶端庫(client library)所需的全部知識(shí), 不過請(qǐng)注意, 這里列出的一部分細(xì)節(jié)可能會(huì)在未來發(fā)生變化。

什么是 Redis 集群?

Redis 集群是一個(gè)分布式(distributed)、容錯(cuò)(fault-tolerant)的 Redis 實(shí)現(xiàn), 集群可以使用的功能是普通單機(jī) Redis 所能使用的功能的一個(gè)子集(subset)。

Redis 集群中不存在中心(central)節(jié)點(diǎn)或者代理(proxy)節(jié)點(diǎn), 集群的其中一個(gè)主要設(shè)計(jì)目標(biāo)是達(dá)到線性可擴(kuò)展性(linear scalability)。

Redis 集群為了保證一致性(consistency)而犧牲了一部分容錯(cuò)性: 系統(tǒng)會(huì)在保證對(duì)網(wǎng)絡(luò)斷線(net split)和節(jié)點(diǎn)失效(node failure)具有有限(limited)抵抗力的前提下, 盡可能地保持?jǐn)?shù)據(jù)的一致性。

集群將節(jié)點(diǎn)失效視為網(wǎng)絡(luò)斷線的其中一種特殊情況。

集群的容錯(cuò)功能是通過使用主節(jié)點(diǎn)(master)和從節(jié)點(diǎn)(slave)兩種角色(role)的節(jié)點(diǎn)(node)來實(shí)現(xiàn)的:

主節(jié)點(diǎn)和從節(jié)點(diǎn)使用完全相同的服務(wù)器實(shí)現(xiàn), 它們的功能(functionally)也完全一樣, 但從節(jié)點(diǎn)通常僅用于替換失效的主節(jié)點(diǎn)。
不過, 如果不需要保證“先寫入,后讀取”操作的一致性(read-after-write consistency), 那么可以使用從節(jié)點(diǎn)來執(zhí)行只讀查詢。

Redis 集群實(shí)現(xiàn)的功能子集

Redis 集群實(shí)現(xiàn)了單機(jī) Redis 中, 所有處理單個(gè)數(shù)據(jù)庫鍵的命令。

針對(duì)多個(gè)數(shù)據(jù)庫鍵的復(fù)雜計(jì)算操作, 比如集合的并集操作、合集操作沒有被實(shí)現(xiàn), 那些理論上需要使用多個(gè)節(jié)點(diǎn)的多個(gè)數(shù)據(jù)庫鍵才能完成的命令也沒有被實(shí)現(xiàn)。

在將來, 用戶也許可以通過 MIGRATE COPY 命令, 在集群的計(jì)算節(jié)點(diǎn)(computation node)中執(zhí)行針對(duì)多個(gè)數(shù)據(jù)庫鍵的只讀操作, 但集群本身不會(huì)去實(shí)現(xiàn)那些需要將多個(gè)數(shù)據(jù)庫鍵在多個(gè)節(jié)點(diǎn)中移來移去的復(fù)雜多鍵命令。

Redis 集群不像單機(jī) Redis 那樣支持多數(shù)據(jù)庫功能, 集群只使用默認(rèn)的 0 號(hào)數(shù)據(jù)庫, 并且不能使用 SELECT 命令。

Redis 集群協(xié)議中的客戶端和服務(wù)器

Redis 集群中的節(jié)點(diǎn)有以下責(zé)任:

持有鍵值對(duì)數(shù)據(jù)。
記錄集群的狀態(tài),包括鍵到正確節(jié)點(diǎn)的映射(mapping keys to right nodes)。
自動(dòng)發(fā)現(xiàn)其他節(jié)點(diǎn),識(shí)別工作不正常的節(jié)點(diǎn),并在有需要時(shí),在從節(jié)點(diǎn)中選舉出新的主節(jié)點(diǎn)。

為了執(zhí)行以上列出的任務(wù), 集群中的每個(gè)節(jié)點(diǎn)都與其他節(jié)點(diǎn)建立起了“集群連接(cluster bus)”, 該連接是一個(gè) TCP 連接, 使用二進(jìn)制協(xié)議進(jìn)行通訊。

節(jié)點(diǎn)之間使用 Gossip 協(xié)議 來進(jìn)行以下工作:

傳播(propagate)關(guān)于集群的信息,以此來發(fā)現(xiàn)新的節(jié)點(diǎn)。
向其他節(jié)點(diǎn)發(fā)送 PING 數(shù)據(jù)包,以此來檢查目標(biāo)節(jié)點(diǎn)是否正常運(yùn)作。
在特定事件發(fā)生時(shí),發(fā)送集群信息。

除此之外, 集群連接還用于在集群中發(fā)布或訂閱信息。

因?yàn)榧汗?jié)點(diǎn)不能代理(proxy)命令請(qǐng)求, 所以客戶端應(yīng)該在節(jié)點(diǎn)返回 -MOVED 或者 -ASK 轉(zhuǎn)向(redirection)錯(cuò)誤時(shí), 自行將命令請(qǐng)求轉(zhuǎn)發(fā)至其他節(jié)點(diǎn)。

因?yàn)榭蛻舳丝梢宰杂傻叵蚣褐械娜魏我粋€(gè)節(jié)點(diǎn)發(fā)送命令請(qǐng)求, 并可以在有需要時(shí), 根據(jù)轉(zhuǎn)向錯(cuò)誤所提供的信息, 將命令轉(zhuǎn)發(fā)至正確的節(jié)點(diǎn), 所以在理論上來說, 客戶端是無須保存集群狀態(tài)信息的。

不過, 如果客戶端可以將鍵和節(jié)點(diǎn)之間的映射信息保存起來, 可以有效地減少可能出現(xiàn)的轉(zhuǎn)向次數(shù), 籍此提升命令執(zhí)行的效率。

鍵分布模型

Redis 集群的鍵空間被分割為 16384 個(gè)槽(slot), 集群的最大節(jié)點(diǎn)數(shù)量也是 16384 個(gè)。

推薦的最大節(jié)點(diǎn)數(shù)量為 1000 個(gè)左右。

每個(gè)主節(jié)點(diǎn)都負(fù)責(zé)處理 16384 個(gè)哈希槽的其中一部分。

當(dāng)我們說一個(gè)集群處于“穩(wěn)定”(stable)狀態(tài)時(shí), 指的是集群沒有在執(zhí)行重配置(reconfiguration)操作, 每個(gè)哈希槽都只由一個(gè)節(jié)點(diǎn)進(jìn)行處理。

重配置指的是將某個(gè)/某些槽從一個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)節(jié)點(diǎn)。

一個(gè)主節(jié)點(diǎn)可以有任意多個(gè)從節(jié)點(diǎn), 這些從節(jié)點(diǎn)用于在主節(jié)點(diǎn)發(fā)生網(wǎng)絡(luò)斷線或者節(jié)點(diǎn)失效時(shí), 對(duì)主節(jié)點(diǎn)進(jìn)行替換。

以下是負(fù)責(zé)將鍵映射到槽的算法:

HASH_SLOT = CRC16(key) mod 16384

以下是該算法所使用的參數(shù):

算法的名稱: XMODEM (又稱 ZMODEM 或者 CRC-16/ACORN)
結(jié)果的長(zhǎng)度: 16 位
多項(xiàng)數(shù)(poly): 1021 (也即是 x16 + x12 + x5 + 1)
初始化值: 0000
反射輸入字節(jié)(Reflect Input byte): False
發(fā)射輸出 CRC (Reflect Output CRC): False
用于 CRC 輸出值的異或常量(Xor constant to output CRC): 0000
該算法對(duì)于輸入 "123456789" 的輸出: 31C3

附錄 A 中給出了集群所使用的 CRC16 算法的實(shí)現(xiàn)。

CRC16 算法所產(chǎn)生的 16 位輸出中的 14 位會(huì)被用到。
在我們的測(cè)試中, CRC16 算法可以很好地將各種不同類型的鍵平穩(wěn)地分布到 16384 個(gè)槽里面。

集群節(jié)點(diǎn)屬性

每個(gè)節(jié)點(diǎn)在集群中都有一個(gè)獨(dú)一無二的 ID , 該 ID 是一個(gè)十六進(jìn)制表示的 160 位隨機(jī)數(shù), 在節(jié)點(diǎn)第一次啟動(dòng)時(shí)由 /dev/urandom 生成。

節(jié)點(diǎn)會(huì)將它的 ID 保存到配置文件, 只要這個(gè)配置文件不被刪除, 節(jié)點(diǎn)就會(huì)一直沿用這個(gè) ID 。

節(jié)點(diǎn) ID 用于標(biāo)識(shí)集群中的每個(gè)節(jié)點(diǎn)。 一個(gè)節(jié)點(diǎn)可以改變它的 IP 和端口號(hào), 而不改變節(jié)點(diǎn) ID 。 集群可以自動(dòng)識(shí)別出 IP/端口號(hào)的變化, 并將這一信息通過 Gossip 協(xié)議廣播給其他節(jié)點(diǎn)知道。

以下是每個(gè)節(jié)點(diǎn)都有的關(guān)聯(lián)信息, 并且節(jié)點(diǎn)會(huì)將這些信息發(fā)送給其他節(jié)點(diǎn):

節(jié)點(diǎn)所使用的 IP 地址和 TCP 端口號(hào)。
節(jié)點(diǎn)的標(biāo)志(flags)。
節(jié)點(diǎn)負(fù)責(zé)處理的哈希槽。
節(jié)點(diǎn)最近一次使用集群連接發(fā)送 PING 數(shù)據(jù)包(packet)的時(shí)間。
節(jié)點(diǎn)最近一次在回復(fù)中接收到 PONG 數(shù)據(jù)包的時(shí)間。
集群將該節(jié)點(diǎn)標(biāo)記為下線的時(shí)間。
該節(jié)點(diǎn)的從節(jié)點(diǎn)數(shù)量。
如果該節(jié)點(diǎn)是從節(jié)點(diǎn)的話,那么它會(huì)記錄主節(jié)點(diǎn)的節(jié)點(diǎn) ID 。 如果這是一個(gè)主節(jié)點(diǎn)的話,那么主節(jié)點(diǎn) ID 這一欄的值為 0000000 。

以上信息的其中一部分可以通過向集群中的任意節(jié)點(diǎn)(主節(jié)點(diǎn)或者從節(jié)點(diǎn)都可以)發(fā)送 CLUSTER NODES 命令來獲得。

以下是一個(gè)向集群中的主節(jié)點(diǎn)發(fā)送 CLUSTER NODES 命令的例子, 該集群由三個(gè)節(jié)點(diǎn)組成:

$ 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

在上面列出的三行信息中, 從左到右的各個(gè)域分別是: 節(jié)點(diǎn) ID , IP 地址和端口號(hào), 標(biāo)志(flag), 最后發(fā)送 PING 的時(shí)間, 最后接收 PONG 的時(shí)間, 連接狀態(tài), 節(jié)點(diǎn)負(fù)責(zé)處理的槽。

節(jié)點(diǎn)握手(已實(shí)現(xiàn))

節(jié)點(diǎn)總是應(yīng)答(accept)來自集群連接端口的連接請(qǐng)求, 并對(duì)接收到的 PING 數(shù)據(jù)包進(jìn)行回復(fù), 即使這個(gè) PING 數(shù)據(jù)包來自不可信的節(jié)點(diǎn)。

然而, 除了 PING 之外, 節(jié)點(diǎn)會(huì)拒絕其他所有并非來自集群節(jié)點(diǎn)的數(shù)據(jù)包。

要讓一個(gè)節(jié)點(diǎn)承認(rèn)另一個(gè)節(jié)點(diǎn)同屬于一個(gè)集群, 只有以下兩種方法:

一個(gè)節(jié)點(diǎn)可以通過向另一個(gè)節(jié)點(diǎn)發(fā)送 MEET 信息, 來強(qiáng)制讓接收信息的節(jié)點(diǎn)承認(rèn)發(fā)送信息的節(jié)點(diǎn)為集群中的一份子。 一個(gè)節(jié)點(diǎn)僅在管理員顯式地向它發(fā)送 CLUSTER MEET ip port 命令時(shí), 才會(huì)向另一個(gè)節(jié)點(diǎn)發(fā)送 MEET 信息。
另外, 如果一個(gè)可信節(jié)點(diǎn)向另一個(gè)節(jié)點(diǎn)傳播第三者節(jié)點(diǎn)的信息, 那么接收信息的那個(gè)節(jié)點(diǎn)也會(huì)將第三者節(jié)點(diǎn)識(shí)別為集群中的一份子。 也即是說, 如果 A 認(rèn)識(shí) B , B 認(rèn)識(shí) C , 并且 B 向 A 傳播關(guān)于 C 的信息, 那么 A 也會(huì)將 C 識(shí)別為集群中的一份子, 并嘗試連接 C 。

這意味著如果我們將一個(gè)/一些新節(jié)點(diǎn)添加到一個(gè)集群中, 那么這個(gè)/這些新節(jié)點(diǎn)最終會(huì)和集群中已有的其他所有節(jié)點(diǎn)連接起來。

這說明只要管理員使用 CLUSTER MEET 命令顯式地指定了可信關(guān)系, 集群就可以自動(dòng)發(fā)現(xiàn)其他節(jié)點(diǎn)。

這種節(jié)點(diǎn)識(shí)別機(jī)制通過防止不同的 Redis 集群因?yàn)?IP 地址變更或者其他網(wǎng)絡(luò)事件的發(fā)生而產(chǎn)生意料之外的聯(lián)合(mix), 從而使得集群更具健壯性。

當(dāng)節(jié)點(diǎn)的網(wǎng)絡(luò)連接斷開時(shí), 它會(huì)主動(dòng)連接其他已知的節(jié)點(diǎn)。

MOVED 轉(zhuǎn)向

一個(gè) Redis 客戶端可以向集群中的任意節(jié)點(diǎn)(包括從節(jié)點(diǎn))發(fā)送命令請(qǐng)求。 節(jié)點(diǎn)會(huì)對(duì)命令請(qǐng)求進(jìn)行分析, 如果該命令是集群可以執(zhí)行的命令, 那么節(jié)點(diǎn)會(huì)查找這個(gè)命令所要處理的鍵所在的槽。

如果要查找的哈希槽正好就由接收到命令的節(jié)點(diǎn)負(fù)責(zé)處理, 那么節(jié)點(diǎn)就直接執(zhí)行這個(gè)命令。
另一方面, 如果所查找的槽不是由該節(jié)點(diǎn)處理的話, 節(jié)點(diǎn)將查看自身內(nèi)部所保存的哈希槽到節(jié)點(diǎn) ID 的映射記錄, 并向客戶

端回復(fù)一個(gè) MOVED 錯(cuò)誤。

以下是一個(gè) MOVED 錯(cuò)誤的例子:

GET x
-MOVED 3999 127.0.0.1:6381

錯(cuò)誤信息包含鍵 x 所屬的哈希槽 3999 , 以及負(fù)責(zé)處理這個(gè)槽的節(jié)點(diǎn)的 IP 和端口號(hào) 127.0.0.1:6381 。 客戶端需要根據(jù)這個(gè) IP 和端口號(hào), 向所屬的節(jié)點(diǎn)重新發(fā)送一次 GET 命令請(qǐng)求。

注意, 即使客戶端在重新發(fā)送 GET 命令之前, 等待了非常久的時(shí)間, 以至于集群又再次更改了配置, 使得節(jié)點(diǎn) 127.0.0.1:6381 已經(jīng)不再處理槽 3999 , 那么當(dāng)客戶端向節(jié)點(diǎn) 127.0.0.1:6381 發(fā)送 GET 命令的時(shí)候, 節(jié)點(diǎn)將再次向客戶端返回 MOVED 錯(cuò)誤, 指示現(xiàn)在負(fù)責(zé)處理槽 3999 的節(jié)點(diǎn)。

雖然我們用 ID 來標(biāo)識(shí)集群中的節(jié)點(diǎn), 但是為了讓客戶端的轉(zhuǎn)向操作盡可能地簡(jiǎn)單, 節(jié)點(diǎn)在 MOVED 錯(cuò)誤中直接返回目標(biāo)節(jié)點(diǎn)的 IP 和端口號(hào), 而不是目標(biāo)節(jié)點(diǎn)的 ID 。

雖然不是必須的, 但一個(gè)客戶端應(yīng)該記錄(memorize)下“槽 3999 由節(jié)點(diǎn) 127.0.0.1:6381 負(fù)責(zé)處理“這一信息, 這樣當(dāng)再次有命令需要對(duì)槽 3999 執(zhí)行時(shí), 客戶端就可以加快尋找正確節(jié)點(diǎn)的速度。

注意, 當(dāng)集群處于穩(wěn)定狀態(tài)時(shí), 所有客戶端最終都會(huì)保存有一個(gè)哈希槽至節(jié)點(diǎn)的映射記錄(map of hash slots to nodes), 使得集群非常高效: 客戶端可以直接向正確的節(jié)點(diǎn)發(fā)送命令請(qǐng)求, 無須轉(zhuǎn)向、代理或者其他任何可能發(fā)生單點(diǎn)故障(single point failure)的實(shí)體(entiy)。

除了 MOVED 轉(zhuǎn)向錯(cuò)誤之外, 一個(gè)客戶端還應(yīng)該可以處理稍后介紹的 ASK 轉(zhuǎn)向錯(cuò)誤。

集群在線重配置(live reconfiguration)

Redis 集群支持在集群運(yùn)行的過程中添加或者移除節(jié)點(diǎn)。

實(shí)際上, 節(jié)點(diǎn)的添加操作和節(jié)點(diǎn)的刪除操作可以抽象成同一個(gè)操作, 那就是, 將哈希槽從一個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)節(jié)點(diǎn):

添加一個(gè)新節(jié)點(diǎn)到集群, 等于將其他已存在節(jié)點(diǎn)的槽移動(dòng)到一個(gè)空白的新節(jié)點(diǎn)里面。

從集群中移除一個(gè)節(jié)點(diǎn), 等于將被移除節(jié)點(diǎn)的所有槽移動(dòng)到集群的其他節(jié)點(diǎn)上面去。

因此, 實(shí)現(xiàn) Redis 集群在線重配置的核心就是將槽從一個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)節(jié)點(diǎn)的能力。 因?yàn)橐粋€(gè)哈希槽實(shí)際上就是一些鍵的集合, 所以 Redis 集群在重哈希(rehash)時(shí)真正要做的, 就是將一些鍵從一個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)節(jié)點(diǎn)。

要理解 Redis 集群如何將槽從一個(gè)節(jié)點(diǎn)移動(dòng)到另一個(gè)節(jié)點(diǎn), 我們需要對(duì) CLUSTER 命令的各個(gè)子命令進(jìn)行介紹, 這些命理負(fù)責(zé)管理集群節(jié)點(diǎn)的槽轉(zhuǎn)換表(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 分別用于向節(jié)點(diǎn)指派(assign)或者移除節(jié)點(diǎn), 當(dāng)槽被指派或者移除之后, 節(jié)點(diǎn)會(huì)將這一信息通過 Gossip 協(xié)議傳播到整個(gè)集群。 ADDSLOTS 命令通常在新創(chuàng)建集群時(shí), 作為一種快速地將各個(gè)槽指派給各個(gè)節(jié)點(diǎn)的手段來使用。

CLUSTER SETSLOT slot NODE node 子命令可以將指定的槽 slot 指派給節(jié)點(diǎn) node 。

至于 CLUSTER SETSLOT slot MIGRATING node 命令和 CLUSTER SETSLOT slot IMPORTING node 命令, 前者用于將給定節(jié)點(diǎn) node 中的槽 slot 遷移出節(jié)點(diǎn), 而后者用于將給定槽 slot 導(dǎo)入到節(jié)點(diǎn) node :

當(dāng)一個(gè)槽被設(shè)置為 MIGRATING 狀態(tài)時(shí), 原來持有這個(gè)槽的節(jié)點(diǎn)仍然會(huì)繼續(xù)接受關(guān)于這個(gè)槽的命令請(qǐng)求, 但只有命令所處理的鍵仍然存在于節(jié)點(diǎn)時(shí), 節(jié)點(diǎn)才會(huì)處理這個(gè)命令請(qǐng)求。

如果命令所使用的鍵不存在與該節(jié)點(diǎn), 那么節(jié)點(diǎn)將向客戶端返回一個(gè) -ASK 轉(zhuǎn)向(redirection)錯(cuò)誤, 告知客戶端, 要將命令請(qǐng)求發(fā)送到槽的遷移目標(biāo)節(jié)點(diǎn)。

當(dāng)一個(gè)槽被設(shè)置為 IMPORTING 狀態(tài)時(shí), 節(jié)點(diǎn)僅在接收到 ASKING 命令之后, 才會(huì)接受關(guān)于這個(gè)槽的命令請(qǐng)求。

如果客戶端沒有向節(jié)點(diǎn)發(fā)送 ASKING 命令, 那么節(jié)點(diǎn)會(huì)使用 -MOVED 轉(zhuǎn)向錯(cuò)誤將命令請(qǐng)求轉(zhuǎn)向至真正負(fù)責(zé)處理這個(gè)槽的節(jié)點(diǎn)。

上面關(guān)于 MIGRATING 和 IMPORTING 的說明有些難懂, 讓我們用一個(gè)實(shí)際的實(shí)例來說明一下。

假設(shè)現(xiàn)在, 我們有 A 和 B 兩個(gè)節(jié)點(diǎn), 并且我們想將槽 8 從節(jié)點(diǎn) A 移動(dòng)到節(jié)點(diǎn) B , 于是我們:

向節(jié)點(diǎn) B 發(fā)送命令 CLUSTER SETSLOT 8 IMPORTING A
向節(jié)點(diǎn) A 發(fā)送命令 CLUSTER SETSLOT 8 MIGRATING B

每當(dāng)客戶端向其他節(jié)點(diǎn)發(fā)送關(guān)于哈希槽 8 的命令請(qǐng)求時(shí), 這些節(jié)點(diǎn)都會(huì)向客戶端返回指向節(jié)點(diǎn) A 的轉(zhuǎn)向信息:

如果命令要處理的鍵已經(jīng)存在于槽 8 里面, 那么這個(gè)命令將由節(jié)點(diǎn) A 處理。
如果命令要處理的鍵未存在于槽 8 里面(比如說,要向槽添加一個(gè)新的鍵), 那么這個(gè)命令由節(jié)點(diǎn) B 處理。

這種機(jī)制將使得節(jié)點(diǎn) A 不再創(chuàng)建關(guān)于槽 8 的任何新鍵。

與此同時(shí), 一個(gè)特殊的客戶端 redis-trib 以及 Redis 集群配置程序(configuration utility)會(huì)將節(jié)點(diǎn) A 中槽 8 里面的鍵移動(dòng)到節(jié)點(diǎn) B 。

鍵的移動(dòng)操作由以下兩個(gè)命令執(zhí)行:

CLUSTER GETKEYSINSLOT slot count

上面的命令會(huì)讓節(jié)點(diǎn)返回 count 個(gè) slot 槽中的鍵, 對(duì)于命令所返回的每個(gè)鍵, redis-trib 都會(huì)向節(jié)點(diǎn) A 發(fā)送一條 MIGRATE 命令, 該命令會(huì)將所指定的鍵原子地(atomic)從節(jié)點(diǎn) A 移動(dòng)到節(jié)點(diǎn) B (在移動(dòng)鍵期間,兩個(gè)節(jié)點(diǎn)都會(huì)處于阻塞狀態(tài),以免出現(xiàn)競(jìng)爭(zhēng)條件)。

以下為 MIGRATE 命令的運(yùn)作原理:

MIGRATE target_host target_port key target_database id timeout

執(zhí)行 MIGRATE 命令的節(jié)點(diǎn)會(huì)連接到 target 節(jié)點(diǎn), 并將序列化后的 key 數(shù)據(jù)發(fā)送給 target , 一旦 target 返回 OK , 節(jié)點(diǎn)就將自己的 key 從數(shù)據(jù)庫中刪除。

從一個(gè)外部客戶端的視角來看, 在某個(gè)時(shí)間點(diǎn)上, 鍵 key 要么存在于節(jié)點(diǎn) A , 要么存在于節(jié)點(diǎn) B , 但不會(huì)同時(shí)存在于節(jié)點(diǎn) A 和節(jié)點(diǎn) B 。

因?yàn)?Redis 集群只使用 0 號(hào)數(shù)據(jù)庫, 所以當(dāng) MIGRATE 命令被用于執(zhí)行集群操作時(shí), target_database 的值總是 0 。
target_database 參數(shù)的存在是為了讓 MIGRATE 命令成為一個(gè)通用命令, 從而可以作用于集群以外的其他功能。

我們對(duì) MIGRATE 命令做了優(yōu)化, 使得它即使在傳輸包含多個(gè)元素的列表鍵這樣的復(fù)雜數(shù)據(jù)時(shí), 也可以保持高效。

不過, 盡管 MIGRATE 非常高效, 對(duì)一個(gè)鍵非常多、并且鍵的數(shù)據(jù)量非常大的集群來說, 集群重配置還是會(huì)占用大量的時(shí)間, 可能會(huì)導(dǎo)致集群沒辦法適應(yīng)那些對(duì)于響應(yīng)時(shí)間有嚴(yán)格要求的應(yīng)用程序。

ASK 轉(zhuǎn)向

在之前介紹 MOVED 轉(zhuǎn)向的時(shí)候, 我們說除了 MOVED 轉(zhuǎn)向之外, 還有另一種 ASK 轉(zhuǎn)向。

當(dāng)節(jié)點(diǎn)需要讓一個(gè)客戶端長(zhǎng)期地(permanently)將針對(duì)某個(gè)槽的命令請(qǐng)求發(fā)送至另一個(gè)節(jié)點(diǎn)時(shí), 節(jié)點(diǎn)向客戶端返回 MOVED 轉(zhuǎn)向。

另一方面, 當(dāng)節(jié)點(diǎn)需要讓客戶端僅僅在下一個(gè)命令請(qǐng)求中轉(zhuǎn)向至另一個(gè)節(jié)點(diǎn)時(shí), 節(jié)點(diǎn)向客戶端返回 ASK 轉(zhuǎn)向。

比如說, 在我們上一節(jié)列舉的槽 8 的例子中, 因?yàn)椴?8 所包含的各個(gè)鍵分散在節(jié)點(diǎn) A 和節(jié)點(diǎn) B 中, 所以當(dāng)客戶端在節(jié)點(diǎn) A 中沒找到某個(gè)鍵時(shí), 它應(yīng)該轉(zhuǎn)向到節(jié)點(diǎn) B 中去尋找, 但是這種轉(zhuǎn)向應(yīng)該僅僅影響一次命令查詢, 而不是讓客戶端每次都直接去查找節(jié)點(diǎn) B : 在節(jié)點(diǎn) A 所持有的屬于槽 8 的鍵沒有全部被遷移到節(jié)點(diǎn) B 之前, 客戶端應(yīng)該先訪問節(jié)點(diǎn) A , 然后再訪問節(jié)點(diǎn) B 。

因?yàn)檫@種轉(zhuǎn)向只針對(duì) 16384 個(gè)槽中的其中一個(gè)槽, 所以轉(zhuǎn)向?qū)涸斐傻男阅軗p耗屬于可接受的范圍。

因?yàn)樯鲜鲈颍?如果我們要在查找節(jié)點(diǎn) A 之后, 繼續(xù)查找節(jié)點(diǎn) B , 那么客戶端在向節(jié)點(diǎn) B 發(fā)送命令請(qǐng)求之前, 應(yīng)該先發(fā)送一個(gè) ASKING 命令, 否則這個(gè)針對(duì)帶有 IMPORTING 狀態(tài)的槽的命令請(qǐng)求將被節(jié)點(diǎn) B 拒絕執(zhí)行。

接收到客戶端 ASKING 命令的節(jié)點(diǎn)將為客戶端設(shè)置一個(gè)一次性的標(biāo)志(flag), 使得客戶端可以執(zhí)行一次針對(duì) IMPORTING 狀態(tài)的槽的命令請(qǐng)求。

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

如果客戶端接收到 ASK 轉(zhuǎn)向, 那么將命令請(qǐng)求的發(fā)送對(duì)象調(diào)整為轉(zhuǎn)向所指定的節(jié)點(diǎn)。
先發(fā)送一個(gè) ASKING 命令,然后再發(fā)送真正的命令請(qǐng)求。
不必更新客戶端所記錄的槽 8 至節(jié)點(diǎn)的映射: 槽 8 應(yīng)該仍然映射到節(jié)點(diǎn) A , 而不是節(jié)點(diǎn) B 。

一旦節(jié)點(diǎn) A 針對(duì)槽 8 的遷移工作完成, 節(jié)點(diǎn) A 在再次收到針對(duì)槽 8 的命令請(qǐng)求時(shí), 就會(huì)向客戶端返回 MOVED 轉(zhuǎn)向, 將關(guān)于槽 8 的命令請(qǐng)求長(zhǎng)期地轉(zhuǎn)向到節(jié)點(diǎn) B 。

注意, 即使客戶端出現(xiàn) Bug , 過早地將槽 8 映射到了節(jié)點(diǎn) B 上面, 但只要這個(gè)客戶端不發(fā)送 ASKING 命令, 客戶端發(fā)送命令請(qǐng)求的時(shí)候就會(huì)遇上 MOVED 錯(cuò)誤, 并將它轉(zhuǎn)向回節(jié)點(diǎn) A 。

容錯(cuò)

節(jié)點(diǎn)失效檢測(cè)

以下是節(jié)點(diǎn)失效檢查的實(shí)現(xiàn)方法:

當(dāng)一個(gè)節(jié)點(diǎn)向另一個(gè)節(jié)點(diǎn)發(fā)送 PING 命令, 但是目標(biāo)節(jié)點(diǎn)未能在給定的時(shí)限內(nèi)返回 PING 命令的回復(fù)時(shí), 那么發(fā)送命令的節(jié)點(diǎn)會(huì)將目標(biāo)節(jié)點(diǎn)標(biāo)記為 PFAIL (possible failure,可能已失效)。
等待 PING 命令回復(fù)的時(shí)限稱為“節(jié)點(diǎn)超時(shí)時(shí)限(node timeout)”, 是一個(gè)節(jié)點(diǎn)選項(xiàng)(node-wise setting)。
每次當(dāng)節(jié)點(diǎn)對(duì)其他節(jié)點(diǎn)發(fā)送 PING 命令的時(shí)候, 它都會(huì)隨機(jī)地廣播三個(gè)它所知道的節(jié)點(diǎn)的信息, 這些信息里面的其中一項(xiàng)就是說明節(jié)點(diǎn)是否已經(jīng)被標(biāo)記為 PFAIL 或者 FAIL 。
當(dāng)節(jié)點(diǎn)接收到其他節(jié)點(diǎn)發(fā)來的信息時(shí), 它會(huì)記下那些被其他節(jié)點(diǎn)標(biāo)記為失效的節(jié)點(diǎn)。 這稱為失效報(bào)告(failure report)。
如果節(jié)點(diǎn)已經(jīng)將某個(gè)節(jié)點(diǎn)標(biāo)記為 PFAIL , 并且根據(jù)節(jié)點(diǎn)所收到的失效報(bào)告顯式, 集群中的大部分其他主節(jié)點(diǎn)也認(rèn)為那個(gè)節(jié)點(diǎn)進(jìn)入了失效狀態(tài), 那么節(jié)點(diǎn)會(huì)將那個(gè)失效節(jié)點(diǎn)的狀態(tài)標(biāo)記為 FAIL 。
一旦某個(gè)節(jié)點(diǎn)被標(biāo)記為 FAIL , 關(guān)于這個(gè)節(jié)點(diǎn)已失效的信息就會(huì)被廣播到整個(gè)集群, 所有接收到這條信息的節(jié)點(diǎn)都會(huì)將失效節(jié)點(diǎn)標(biāo)記為 FAIL 。

簡(jiǎn)單來說, 一個(gè)節(jié)點(diǎn)要將另一個(gè)節(jié)點(diǎn)標(biāo)記為失效, 必須先詢問其他節(jié)點(diǎn)的意見, 并且得到大部分主節(jié)點(diǎn)的同意才行。

因?yàn)檫^期的失效報(bào)告會(huì)被移除, 所以主節(jié)點(diǎn)要將某個(gè)節(jié)點(diǎn)標(biāo)記為 FAIL 的話, 必須以最近接收到的失效報(bào)告作為根據(jù)。

在以下兩種情況中, 節(jié)點(diǎn)的 FAIL 狀態(tài)會(huì)被移除:

如果被標(biāo)記為 FAIL 的是從節(jié)點(diǎn), 那么當(dāng)這個(gè)節(jié)點(diǎn)重新上線時(shí), FAIL 標(biāo)記就會(huì)被移除。
保持(retaning)從節(jié)點(diǎn)的 FAIL 狀態(tài)是沒有意義的, 因?yàn)樗惶幚砣魏尾郏?一個(gè)從節(jié)點(diǎn)是否處于 FAIL 狀態(tài), 決定了這個(gè)從節(jié)點(diǎn)在有需要時(shí)能否被提升為主節(jié)點(diǎn)。
如果一個(gè)主節(jié)點(diǎn)被打上 FAIL 標(biāo)記之后, 經(jīng)過了節(jié)點(diǎn)超時(shí)時(shí)限的四倍時(shí)間, 再加上十秒鐘之后, 針對(duì)這個(gè)主節(jié)點(diǎn)的槽的故障轉(zhuǎn)移操作仍未完成, 并且這個(gè)主節(jié)點(diǎn)已經(jīng)重新上線的話, 那么移除對(duì)這個(gè)節(jié)點(diǎn)的 FAIL 標(biāo)記。

在第二種情況中, 如果故障轉(zhuǎn)移未能順利完成, 并且主節(jié)點(diǎn)重新上線, 那么集群就繼續(xù)使用原來的主節(jié)點(diǎn), 從而免去管理員介入的必要。

集群狀態(tài)檢測(cè)(已部分實(shí)現(xiàn))

每當(dāng)集群發(fā)生配置變化時(shí)(可能是哈希槽更新,也可能是某個(gè)節(jié)點(diǎn)進(jìn)入失效狀態(tài)), 集群中的每個(gè)節(jié)點(diǎn)都會(huì)對(duì)它所知道的節(jié)點(diǎn)進(jìn)行掃描(scan)。

一旦配置處理完畢, 集群會(huì)進(jìn)入以下兩種狀態(tài)的其中一種:

FAIL : 集群不能正常工作。 當(dāng)集群中有某個(gè)節(jié)點(diǎn)進(jìn)入失效狀態(tài)時(shí), 集群不能處理任何命令請(qǐng)求, 對(duì)于每個(gè)命令請(qǐng)求, 集群節(jié)點(diǎn)都返回錯(cuò)誤回復(fù)。
OK : 集群可以正常工作, 負(fù)責(zé)處理全部 16384 個(gè)槽的節(jié)點(diǎn)中, 沒有一個(gè)節(jié)點(diǎn)被標(biāo)記為 FAIL 狀態(tài)。

這說明即使集群中只有一部分哈希槽不能正常使用, 整個(gè)集群也會(huì)停止處理任何命令。

不過節(jié)點(diǎn)從出現(xiàn)問題到被標(biāo)記為 FAIL 狀態(tài)的這段時(shí)間里, 集群仍然會(huì)正常運(yùn)作, 所以集群在某些時(shí)候, 仍然有可能只能處理針對(duì) 16384 個(gè)槽的其中一個(gè)子集的命令請(qǐng)求。

以下是集群進(jìn)入 FAIL 狀態(tài)的兩種情況:

至少有一個(gè)哈希槽不可用,因?yàn)樨?fù)責(zé)處理這個(gè)槽的節(jié)點(diǎn)進(jìn)入了 FAIL 狀態(tài)。
集群中的大部分主節(jié)點(diǎn)都進(jìn)入下線狀態(tài)。當(dāng)大部分主節(jié)點(diǎn)都進(jìn)入 PFAIL 狀態(tài)時(shí),集群也會(huì)進(jìn)入 FAIL 狀態(tài)。

第二個(gè)檢查是必須的, 因?yàn)橐獙⒁粋€(gè)節(jié)點(diǎn)從 PFAIL 狀態(tài)改變?yōu)?FAIL 狀態(tài), 必須要有大部分主節(jié)點(diǎn)進(jìn)行投票表決, 但是, 當(dāng)集群中的大部分主節(jié)點(diǎn)都進(jìn)入失效狀態(tài)時(shí), 單憑一個(gè)兩個(gè)節(jié)點(diǎn)是沒有辦法將一個(gè)節(jié)點(diǎn)標(biāo)記為 FAIL 狀態(tài)的。

因此, 有了第二個(gè)檢查條件, 只要集群中的大部分主節(jié)點(diǎn)進(jìn)入了下線狀態(tài), 那么集群就可以在不請(qǐng)求這些主節(jié)點(diǎn)的意見下, 將某個(gè)節(jié)點(diǎn)判斷為 FAIL 狀態(tài), 從而讓整個(gè)集群停止處理命令請(qǐng)求。

從節(jié)點(diǎn)選舉

一旦某個(gè)主節(jié)點(diǎn)進(jìn)入 FAIL 狀態(tài), 如果這個(gè)主節(jié)點(diǎn)有一個(gè)或多個(gè)從節(jié)點(diǎn)存在, 那么其中一個(gè)從節(jié)點(diǎn)會(huì)被升級(jí)為新的主節(jié)點(diǎn), 而其他從節(jié)點(diǎn)則會(huì)開始對(duì)這個(gè)新的主節(jié)點(diǎn)進(jìn)行復(fù)制。

新的主節(jié)點(diǎn)由已下線主節(jié)點(diǎn)屬下的所有從節(jié)點(diǎn)中自行選舉產(chǎn)生, 以下是選舉的條件:

這個(gè)節(jié)點(diǎn)是已下線主節(jié)點(diǎn)的從節(jié)點(diǎn)。
已下線主節(jié)點(diǎn)負(fù)責(zé)處理的槽數(shù)量非空。
從節(jié)點(diǎn)的數(shù)據(jù)被認(rèn)為是可靠的, 也即是, 主從節(jié)點(diǎn)之間的復(fù)制連接(replication link)的斷線時(shí)長(zhǎng)不能超過節(jié)點(diǎn)超時(shí)時(shí)限(node timeout)乘以 REDIS_CLUSTER_SLAVE_VALIDITY_MULT 常量得出的積。

如果一個(gè)從節(jié)點(diǎn)滿足了以上的所有條件, 那么這個(gè)從節(jié)點(diǎn)將向集群中的其他主節(jié)點(diǎn)發(fā)送授權(quán)請(qǐng)求, 詢問它們, 是否允許自己(從節(jié)點(diǎn))升級(jí)為新的主節(jié)點(diǎn)。

如果發(fā)送授權(quán)請(qǐng)求的從節(jié)點(diǎn)滿足以下屬性, 那么主節(jié)點(diǎn)將向從節(jié)點(diǎn)返回 FAILOVER_AUTH_GRANTED 授權(quán), 同意從節(jié)點(diǎn)的

升級(jí)要求:

發(fā)送授權(quán)請(qǐng)求的是一個(gè)從節(jié)點(diǎn), 并且它所屬的主節(jié)點(diǎn)處于 FAIL 狀態(tài)。

在已下線主節(jié)點(diǎn)的所有從節(jié)點(diǎn)中, 這個(gè)從節(jié)點(diǎn)的節(jié)點(diǎn) ID 在排序中是最小的。

這個(gè)從節(jié)點(diǎn)處于正常的運(yùn)行狀態(tài): 它沒有被標(biāo)記為 FAIL 狀態(tài), 也沒有被標(biāo)記為 PFAIL 狀態(tài)。

一旦某個(gè)從節(jié)點(diǎn)在給定的時(shí)限內(nèi)得到大部分主節(jié)點(diǎn)的授權(quán), 它就會(huì)開始執(zhí)行以下故障轉(zhuǎn)移操作:

通過 PONG 數(shù)據(jù)包(packet)告知其他節(jié)點(diǎn), 這個(gè)節(jié)點(diǎn)現(xiàn)在是主節(jié)點(diǎn)了。

通過 PONG 數(shù)據(jù)包告知其他節(jié)點(diǎn), 這個(gè)節(jié)點(diǎn)是一個(gè)已升級(jí)的從節(jié)點(diǎn)(promoted slave)。

接管(claiming)所有由已下線主節(jié)點(diǎn)負(fù)責(zé)處理的哈希槽。

顯式地向所有節(jié)點(diǎn)廣播一個(gè) PONG 數(shù)據(jù)包, 加速其他節(jié)點(diǎn)識(shí)別這個(gè)節(jié)點(diǎn)的進(jìn)度, 而不是等待定時(shí)的 PING / PONG 數(shù)據(jù)包。

所有其他節(jié)點(diǎn)都會(huì)根據(jù)新的主節(jié)點(diǎn)對(duì)配置進(jìn)行相應(yīng)的更新,特別地:

所有被新的主節(jié)點(diǎn)接管的槽會(huì)被更新。

已下線主節(jié)點(diǎn)的所有從節(jié)點(diǎn)會(huì)察覺到 PROMOTED 標(biāo)志, 并開始對(duì)新的主節(jié)點(diǎn)進(jìn)行復(fù)制。

如果已下線的主節(jié)點(diǎn)重新回到上線狀態(tài), 那么它會(huì)察覺到 PROMOTED 標(biāo)志, 并將自身調(diào)整為現(xiàn)任主節(jié)點(diǎn)的從節(jié)點(diǎn)。

在集群的生命周期中, 如果一個(gè)帶有 PROMOTED 標(biāo)識(shí)的主節(jié)點(diǎn)因?yàn)槟承┰蜣D(zhuǎn)變成了從節(jié)點(diǎn), 那么該節(jié)點(diǎn)將丟失它所帶有的 PROMOTED 標(biāo)識(shí)。

發(fā)布/訂閱(已實(shí)現(xiàn),但仍然需要改善)

在一個(gè) Redis 集群中, 客戶端可以訂閱任意一個(gè)節(jié)點(diǎn), 也可以向任意一個(gè)節(jié)點(diǎn)發(fā)送信息, 節(jié)點(diǎn)會(huì)對(duì)客戶端所發(fā)送的信息進(jìn)行轉(zhuǎn)發(fā)。

在目前的實(shí)現(xiàn)中, 節(jié)點(diǎn)會(huì)將接收到的信息廣播至集群中的其他所有節(jié)點(diǎn), 在將來的實(shí)現(xiàn)中, 可能會(huì)使用 bloom filter 或者其他算法來優(yōu)化這一操作。

附錄 A: CRC16 算法的 ANSI 實(shí)現(xiàn)參考

/*
 * 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;
}

總結(jié)

以上就是本文關(guān)于redis集群規(guī)范詳解的全部?jī)?nèi)容,希望對(duì)大家有所幫助。感興趣的朋友可以參閱:詳細(xì)分析Redis集群故障、Redis源碼解析:集群手動(dòng)故障轉(zhuǎn)移、從節(jié)點(diǎn)遷移詳解、簡(jiǎn)述Redis和MySQL的區(qū)別等,如有不足之處,請(qǐng)留言指出,小編悉心更正。感謝朋友們對(duì)億速云網(wǎng)站的支持!

向AI問一下細(xì)節(jié)
推薦閱讀:
  1. redis集群安裝
  2. redis集群

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI