<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          Redis 集群篇

          共 15099字,需瀏覽 31分鐘

           ·

          2021-04-08 02:32

          點(diǎn)擊藍(lán)色“JavaKeeper”關(guān)注我喲加個(gè)“星標(biāo)”,一起成長,做牛逼閃閃的技術(shù)人c9a1e2396a02e253db43fa7dd4e7fd2e.webp

          Keeper導(dǎo)讀:我們先回顧下前邊介紹的幾種 Redis 高可用方案:持久化、主從同步和哨兵機(jī)制。但這些方案仍有痛點(diǎn),其中最主要的問題就是存儲(chǔ)能力受單機(jī)限制,以及沒辦法實(shí)現(xiàn)寫操作的負(fù)載均衡。

          Redis 集群剛好解決了上述問題,實(shí)現(xiàn)了較為完善的高可用方案。

          一、Redis 集群是啥

          1.1 Redis 集群化

          集群,即 Redis Cluster,是 Redis 3.0 開始引入的分布式存儲(chǔ)方案。

          集群由多個(gè)節(jié)點(diǎn)(Node)組成,Redis的數(shù)據(jù)分布在這些節(jié)點(diǎn)中。集群中的節(jié)點(diǎn)分為主節(jié)點(diǎn)和從節(jié)點(diǎn):只有主節(jié)點(diǎn)負(fù)責(zé)讀寫請求和集群信息的維護(hù);從節(jié)點(diǎn)只進(jìn)行主節(jié)點(diǎn)數(shù)據(jù)和狀態(tài)信息的復(fù)制。

          1.2 集群的主要作用

          1. 數(shù)據(jù)分區(qū):數(shù)據(jù)分區(qū) (或稱數(shù)據(jù)分片) 是集群最核心的功能。集群將數(shù)據(jù)分散到多個(gè)節(jié)點(diǎn),一方面 突破了 Redis 單機(jī)內(nèi)存大小的限制,存儲(chǔ)容量大大增加另一方面 每個(gè)主節(jié)點(diǎn)都可以對外提供讀服務(wù)和寫服務(wù),大大提高了集群的響應(yīng)能力

            Redis 單機(jī)內(nèi)存大小受限問題,例如,如果單機(jī)內(nèi)存太大,bgsavebgrewriteaoffork 操作可能導(dǎo)致主進(jìn)程阻塞,主從環(huán)境下主機(jī)切換時(shí)可能導(dǎo)致從節(jié)點(diǎn)長時(shí)間無法提供服務(wù),全量復(fù)制階段主節(jié)點(diǎn)的復(fù)制緩沖區(qū)可能溢出……

          2. 高可用:集群支持主從復(fù)制和主節(jié)點(diǎn)的 自動(dòng)故障轉(zhuǎn)移 (與哨兵類似),當(dāng)任一節(jié)點(diǎn)發(fā)生故障時(shí),集群仍然可以對外提供服務(wù)。

          3c1826f085251eab428192c24ebd6242.webp

          上圖展示了 Redis Cluster 典型的架構(gòu)圖,集群中的每一個(gè) Redis 節(jié)點(diǎn)都 互相兩兩相連,客戶端任意 直連 到集群中的 任意一臺(tái),就可以對其他 Redis 節(jié)點(diǎn)進(jìn)行 讀寫 的操作。

          1.3 Redis 集群的基本原理

          261c71cd34308841713d0666d931fd12.webp

          Redis 集群中內(nèi)置了 16384 個(gè)哈希槽。當(dāng)客戶端連接到 Redis 集群之后,會(huì)同時(shí)得到一份關(guān)于這個(gè) 集群的配置信息,當(dāng)客戶端具體對某一個(gè) key 值進(jìn)行操作時(shí),會(huì)計(jì)算出它的一個(gè) Hash 值,然后把結(jié)果對 16384 求余數(shù),這樣每個(gè) key 都會(huì)對應(yīng)一個(gè)編號在 0-16383 之間的哈希槽,Redis 會(huì)根據(jù)節(jié)點(diǎn)數(shù)量 大致均等 的將哈希槽映射到不同的節(jié)點(diǎn)。

          再結(jié)合集群的配置信息就能夠知道這個(gè) key 值應(yīng)該存儲(chǔ)在哪一個(gè)具體的 Redis 節(jié)點(diǎn)中,如果不屬于自己管,那么就會(huì)使用一個(gè)特殊的 MOVED 命令來進(jìn)行一個(gè)跳轉(zhuǎn),告訴客戶端去連接這個(gè)節(jié)點(diǎn)以獲取數(shù)據(jù):

          GET?x
          -MOVED?3999?127.0.0.1:6381

          MOVED 指令第一個(gè)參數(shù) 3999key 對應(yīng)的槽位編號,后面是目標(biāo)節(jié)點(diǎn)地址,MOVED 命令前面有一個(gè)減號,表示這是一個(gè)錯(cuò)誤的消息。客戶端在收到 MOVED 指令后,就立即糾正本地的 槽位映射表,那么下一次再訪問 key 時(shí)就能夠到正確的地方去獲取了。

          二、Hello World

          2.1 創(chuàng)建集群節(jié)點(diǎn)配置文件

          創(chuàng)建六個(gè)配置文件,分別命名為:redis_7000.conf/redis_7001.conf…..redis_7005.conf,然后根據(jù)不同的端口號修改對應(yīng)的端口值就好了(方便管理可以將這些配置文件放在同一個(gè)目錄下,我這里放在了 cluster_config 目錄下):

          #?后臺(tái)執(zhí)行
          daemonize?yes
          #?端口號
          port?7000
          #?啟動(dòng)集群模式
          cluster-enabled?yes
          #?每一個(gè)集群節(jié)點(diǎn)都有一個(gè)配置文件,這個(gè)文件是不能手動(dòng)編輯的。確保每一個(gè)集群節(jié)點(diǎn)的配置文件不通
          cluster-config-file?nodes-7000.conf
          #?集群節(jié)點(diǎn)的超時(shí)時(shí)間,單位:ms,超時(shí)后集群會(huì)認(rèn)為該節(jié)點(diǎn)失敗
          cluster-node-timeout?5000
          #?最后將?appendonly?改成?yes(AOF?持久化)
          appendonly?yes

          2.2 啟動(dòng) Redis 實(shí)例

          啟動(dòng)剛才配置的 6 個(gè) Redis 實(shí)例

          redis-server?cluster_config/redis_7000.conf
          redis-server?cluster_config/redis_7001.conf
          redis-server?cluster_config/redis_7002.conf
          redis-server?cluster_config/redis_7003.conf
          redis-server?cluster_config/redis_7004.conf
          redis-server?cluster_config/redis_7005.conf?

          然后執(zhí)行 ps -ef | grep redis 查看是否啟動(dòng)成功:

          0903242ad973204b4aa1be5671a71dae.webp

          可以看到 6 個(gè) Redis 節(jié)點(diǎn)都以集群的方式成功啟動(dòng)了,但是現(xiàn)在每個(gè)節(jié)點(diǎn)還處于獨(dú)立的狀態(tài),也就是說它們每一個(gè)都各自成了一個(gè)集群,還沒有互相聯(lián)系起來,我們需要手動(dòng)地把他們之間建立起聯(lián)系。

          2.3 建立集群

          創(chuàng)建集群,其實(shí)就是節(jié)點(diǎn)執(zhí)行下列命令(Redis 5 之后的方式,之前的版本可以使用 redis-trib.rb 創(chuàng)建):

          redis-cli?--cluster?create?127.0.0.1:7000?127.0.0.1:7001?127.0.0.1:7002?127.0.0.1:7003?127.0.0.1:7004?127.0.0.1:7005?--cluster-replicas?1

          這里稍微解釋一下這個(gè) --replicas 1 的意思是:我們希望為集群中的每個(gè)主節(jié)點(diǎn)創(chuàng)建一個(gè)從節(jié)點(diǎn)。

          觀察控制臺(tái)輸出:

          761b18775edb5e847b77903865e49b51.webp

          看到 [OK] 的信息之后,就表示集群已經(jīng)搭建成功了,可以看到,這里我們正確地創(chuàng)建了三主三從的集群。

          (這里可能會(huì)遇到一些坑,槽沒有被完全覆蓋,或者 node 不為空這種錯(cuò)誤)

          2.4 驗(yàn)證集群

          我們先使用 redic-cli 任意連接一個(gè)節(jié)點(diǎn):

          redis-cli?-c?-h?127.0.0.1?-p?7000
          127.0.0.1:7000>

          -c 表示集群模式;-h 指定 ip 地址;-p 指定端口。

          然后隨便 set 一些值觀察控制臺(tái)輸入:

          127.0.0.1:7000>?set?name?javakeeper
          ->?Redirected?to?slot?[5798]?located?at?127.0.0.1:7001
          OK
          127.0.0.1:7001>?

          可以看到這里 Redis 自動(dòng)幫我們進(jìn)行了 Redirected 操作跳轉(zhuǎn)到了 7001 這個(gè)實(shí)例上。

          我們再使用 cluster info (查看集群信息)cluster nodes (查看節(jié)點(diǎn)列表) 來分別看看:(任意節(jié)點(diǎn)輸入均可)

          58f76af45b953f67571183edb9f4bb30.webp

          三、深入集群原理

          Redis 集群最核心的功能就是數(shù)據(jù)分區(qū),數(shù)據(jù)分區(qū)之后又伴隨著通信機(jī)制和數(shù)據(jù)結(jié)構(gòu)的建設(shè),所以我們從這 3 個(gè)方面來一一深入

          3.1 數(shù)據(jù)分區(qū)方案

          數(shù)據(jù)分區(qū)有順序分區(qū)哈希分區(qū)等,其中哈希分區(qū)由于其天然的隨機(jī)性,使用廣泛;集群的分區(qū)方案便是哈希分區(qū)的一種。

          哈希分區(qū)的基本思路是:對數(shù)據(jù)的特征值(如key)進(jìn)行哈希,然后根據(jù)哈希值決定數(shù)據(jù)落在哪個(gè)節(jié)點(diǎn)。常見的哈希分區(qū)包括:哈希取余分區(qū)、一致性哈希分區(qū)、帶虛擬節(jié)點(diǎn)的一致性哈希分區(qū)等。

          方案一:哈希取余分區(qū)

          哈希取余分區(qū)思路非常簡單:計(jì)算 key 的 hash 值,然后對節(jié)點(diǎn)數(shù)量進(jìn)行取余,從而決定數(shù)據(jù)映射到哪個(gè)節(jié)點(diǎn)上。

          不過該方案最大的問題是,當(dāng)新增或刪減節(jié)點(diǎn)時(shí),節(jié)點(diǎn)數(shù)量發(fā)生變化,系統(tǒng)中所有的數(shù)據(jù)都需要 重新計(jì)算映射關(guān)系,引發(fā)大規(guī)模數(shù)據(jù)遷移。

          這種方式的突出優(yōu)點(diǎn)是簡單性,常用于數(shù)據(jù)庫的分庫分表規(guī)則,一般采 用預(yù)分區(qū)的方式,提前根據(jù)數(shù)據(jù)量規(guī)劃好分區(qū)數(shù),比如劃分為 512 ?或1024 張表,保證可支撐未來一段時(shí)間的數(shù)據(jù)量,再根據(jù)負(fù)載情況將表遷移到其他數(shù) 據(jù)庫中。擴(kuò)容時(shí)通常采用翻倍擴(kuò)容,避免數(shù)據(jù)映射全部被打亂導(dǎo)致全量遷移的情況

          方案二:一致性哈希分區(qū)

          一致性哈希算法將 整個(gè)哈希值空間 組織成一個(gè)虛擬的圓環(huán),范圍一般是 0 - ,對于每一個(gè)數(shù)據(jù),根據(jù) key 計(jì)算 hash 值,確定數(shù)據(jù)在環(huán)上的位置,然后從此位置沿順時(shí)針行走,找到的第一臺(tái)服務(wù)器就是其應(yīng)該映射到的服務(wù)器:

          b261670cd55d4675d02a679facaf2dad.webp

          與哈希取余分區(qū)相比,一致性哈希分區(qū)將 增減節(jié)點(diǎn)的影響限制在相鄰節(jié)點(diǎn)。以上圖為例,如果在 node1node2 之間增加 node5,則只有 node2 中的一部分?jǐn)?shù)據(jù)會(huì)遷移到 node5;如果去掉 node2,則原 node2 中的數(shù)據(jù)只會(huì)遷移到 node4 中,只有 node4 會(huì)受影響。

          一致性哈希分區(qū)的主要問題在于,當(dāng) 節(jié)點(diǎn)數(shù)量較少 時(shí),增加或刪減節(jié)點(diǎn),對單個(gè)節(jié)點(diǎn)的影響可能很大,造成數(shù)據(jù)的嚴(yán)重不平衡。還是以上圖為例,如果去掉 node2node4 中的數(shù)據(jù)由總數(shù)據(jù)的 1/4 左右變?yōu)?1/2 左右,與其他節(jié)點(diǎn)相比負(fù)載過高。

          方案三:帶有虛擬節(jié)點(diǎn)的一致性哈希分區(qū)

          該方案在 一致性哈希分區(qū)的基礎(chǔ)上,引入了 虛擬節(jié)點(diǎn) 的概念。Redis 集群使用的便是該方案,其中的虛擬節(jié)點(diǎn)稱為 槽(slot)。槽是介于數(shù)據(jù)和實(shí)際節(jié)點(diǎn)之間的虛擬概念,每個(gè)實(shí)際節(jié)點(diǎn)包含一定數(shù)量的槽,每個(gè)槽包含哈希值在一定范圍內(nèi)的數(shù)據(jù)。槽的范圍一般遠(yuǎn)大于節(jié)點(diǎn)數(shù)。

          在使用了槽的一致性哈希分區(qū)中,槽是數(shù)據(jù)管理和遷移的基本單位。槽 解耦數(shù)據(jù)和實(shí)際節(jié)點(diǎn) 之間的關(guān)系,增加或刪除節(jié)點(diǎn)對系統(tǒng)的影響很小。仍以上圖為例,系統(tǒng)中有 4 個(gè)實(shí)際節(jié)點(diǎn),假設(shè)為其分配 16 個(gè)槽(0-15);

          • 槽 0-3 位于 node1;4-7 位于 node2;以此類推….

          如果此時(shí)刪除 node2,只需要將槽 4-7 重新分配即可,例如槽 4-5 分配給 node1,槽 6 分配給 node3,槽 7 分配給 node4;可以看出刪除 node2 后,數(shù)據(jù)在其他節(jié)點(diǎn)的分布仍然較為均衡。

          Redis 虛擬槽分區(qū)的特點(diǎn):

          • 解耦數(shù)據(jù)和節(jié)點(diǎn)之間的關(guān)系,簡化了節(jié)點(diǎn)擴(kuò)容和收縮難度。

          • 節(jié)點(diǎn)自身維護(hù)槽的映射關(guān)系,不需要客戶端或者代理服務(wù)維護(hù)槽分區(qū)元數(shù)據(jù)。

          • 支持節(jié)點(diǎn)、槽、鍵之間的映射查詢,用于數(shù)據(jù)路由、在線伸縮等場景。

          3.2 集群功能限制

          Redis 集群相對單機(jī)在功能上存在一些限制,需要開發(fā)人員提前了解,在使用時(shí)做好規(guī)避。限制如下:

          • key 批量操作支持有限。如 mset、mget,目前只支持具有相同 slot 值的 key 執(zhí)行批量操作。對于映射為不同 slot 值的 key 由于執(zhí)行 mget、mget 等操作可能存在于多個(gè)節(jié)點(diǎn)上因此不被支持。

            (為此,Redis 引入 HashTag 的概念,使得數(shù)據(jù)分布算法可以根據(jù) key 的某一部分進(jìn)行計(jì)算,讓相關(guān)的兩條記錄落到同一個(gè)數(shù)據(jù)分片,當(dāng)一個(gè)key包含 {} 的時(shí)候,就不對整個(gè) key 做 hash,而僅對 {} 包括的字符串做 hash。Pipeline 同樣可以受益于 hash_tag)

            127.0.0.1:7000>?mset?javaframework?Spring?cframework?Libevent
            (error)?CROSSSLOT?Keys?in?request?don't?hash?to?the?same?slot
            127.0.0.1:7000>?mset?java{framework}?Spring?c{framework}?Libevent
            ->?Redirected?to?slot?[10840]?located?at?127.0.0.1:7001
            OK
            127.0.0.1:7001>?mget?java{framework}?c{framework}
            1)?"Spring"
            2)?"Libevent"
            127.0.0.1:7001>?
          • key 事務(wù)操作支持有限。同理只支持多 key 在同一節(jié)點(diǎn)上的事務(wù)操作,當(dāng)多個(gè) key 分布在不同的節(jié)點(diǎn)上時(shí)無法使用事務(wù)功能。

          • key 作為數(shù)據(jù)分區(qū)的最小粒度,因此不能將一個(gè)大的鍵值對象如 hash、list 等映射到不同的節(jié)點(diǎn)。

          • 不支持多數(shù)據(jù)庫空間。單機(jī)下的 Redis 可以支持 16 個(gè)數(shù)據(jù)庫,集群模式下只能使用一個(gè)數(shù)據(jù)庫空間,即 db0。

          • 復(fù)制結(jié)構(gòu)只支持一層,從節(jié)點(diǎn)只能復(fù)制主節(jié)點(diǎn),不支持嵌套樹狀復(fù)制結(jié)構(gòu)。

          3.3 節(jié)點(diǎn)通信

          集群的建立離不開節(jié)點(diǎn)之間的通信,例如我們上面啟動(dòng)六個(gè)集群節(jié)點(diǎn)之后通過 redis-cli 命令幫助我們搭建起來了集群,實(shí)際上背后每個(gè)集群之間的兩兩連接是通過了 CLUSTER MEET 命令發(fā)送 MEET 消息完成的。

          通信過程說明:

          1. 集群中的每個(gè)節(jié)點(diǎn)都會(huì)單獨(dú)開辟一個(gè) TCP 通道,用于節(jié)點(diǎn)之間彼此通信,通信端口號在基礎(chǔ)端口上加 10000
          2. 每個(gè)節(jié)點(diǎn)在固定周期內(nèi)通過特定規(guī)則選擇幾個(gè)節(jié)點(diǎn)發(fā)送 ping 消息
          3. 接收到 ping 消息的節(jié)點(diǎn)用 pong 消息作為響應(yīng)

          集群中每個(gè)節(jié)點(diǎn)通過一定規(guī)則挑選要通信的節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)可能知道全部節(jié)點(diǎn),也可能僅知道部分節(jié)點(diǎn),只要這些節(jié)點(diǎn)彼此可以正常通信,最終它們會(huì)達(dá)到一致的狀態(tài)。當(dāng)節(jié)點(diǎn)出故障、新節(jié)點(diǎn)加入、主從角色變化、槽信息 變更等事件發(fā)生時(shí),通過不斷的 ping/pong 消息通信,經(jīng)過一段時(shí)間后所有的 節(jié)點(diǎn)都會(huì)知道整個(gè)集群全部節(jié)點(diǎn)的最新狀態(tài),從而達(dá)到集群狀態(tài)同步的目 的。

          兩個(gè)端口

          哨兵系統(tǒng) 中,節(jié)點(diǎn)分為 數(shù)據(jù)節(jié)點(diǎn)哨兵節(jié)點(diǎn):前者存儲(chǔ)數(shù)據(jù),后者實(shí)現(xiàn)額外的控制功能。在 集群 中,沒有數(shù)據(jù)節(jié)點(diǎn)與非數(shù)據(jù)節(jié)點(diǎn)之分:所有的節(jié)點(diǎn)都存儲(chǔ)數(shù)據(jù),也都參與集群狀態(tài)的維護(hù)。為此,集群中的每個(gè)節(jié)點(diǎn),都提供了兩個(gè) TCP 端口:

          • 普通端口: 即我們在前面指定的端口 (7000等)。普通端口主要用于為客戶端提供服務(wù) (與單機(jī)節(jié)點(diǎn)類似);但在節(jié)點(diǎn)間數(shù)據(jù)遷移時(shí)也會(huì)使用。
          • 集群端口: 端口號是普通端口 + 10000 (10000是固定值,無法改變),如 7000 節(jié)點(diǎn)的集群端口為 17000集群端口只用于節(jié)點(diǎn)之間的通信,如搭建集群、增減節(jié)點(diǎn)、故障轉(zhuǎn)移等操作時(shí)節(jié)點(diǎn)間的通信;不要使用客戶端連接集群接口。為了保證集群可以正常工作,在配置防火墻時(shí),要同時(shí)開啟普通端口和集群端口。

          Gossip 協(xié)議

          節(jié)點(diǎn)間通信,按照通信協(xié)議可以分為幾種類型:單對單、廣播、Gossip 協(xié)議等。重點(diǎn)是廣播和 Gossip 的對比。

          • 廣播是指向集群內(nèi)所有節(jié)點(diǎn)發(fā)送消息。優(yōu)點(diǎn) 是集群的收斂速度快(集群收斂是指集群內(nèi)所有節(jié)點(diǎn)獲得的集群信息是一致的),缺點(diǎn) 是每條消息都要發(fā)送給所有節(jié)點(diǎn),CPU、帶寬等消耗較大。

          • Gossip 協(xié)議的特點(diǎn)是:在節(jié)點(diǎn)數(shù)量有限的網(wǎng)絡(luò)中,每個(gè)節(jié)點(diǎn)都 “隨機(jī)” 的與部分節(jié)點(diǎn)通信 (并不是真正的隨機(jī),而是根據(jù)特定的規(guī)則選擇通信的節(jié)點(diǎn)),經(jīng)過一番雜亂無章的通信,每個(gè)節(jié)點(diǎn)的狀態(tài)很快會(huì)達(dá)到一致。Gossip 協(xié)議的 優(yōu)點(diǎn) 有負(fù)載 (比廣播) 低、去中心化、容錯(cuò)性高 (因?yàn)橥ㄐ庞腥哂? 等;缺點(diǎn) 主要是集群的收斂速度慢。

            (為什么需要隨機(jī)呢?)

            Gossip 協(xié)議工作原理就是節(jié)點(diǎn)彼此不斷通信交換信息,一段時(shí)間后所有的節(jié)點(diǎn)都會(huì)知道集群完整的信息,這種方式類似流言傳播。

          Gossip 協(xié)議的主要職責(zé)就是信息交換。信息交換的載體就是節(jié)點(diǎn)彼此發(fā)送的 Gossip 消息,了解這些消息有助于我們理解集群如何完成信息交換。

          消息類型

          集群中的節(jié)點(diǎn)采用 固定頻率(每秒10次)定時(shí)任務(wù) 進(jìn)行通信相關(guān)的工作:判斷是否需要發(fā)送消息及消息類型、確定接收節(jié)點(diǎn)、發(fā)送消息等。如果集群狀態(tài)發(fā)生了變化,如增減節(jié)點(diǎn)、槽狀態(tài)變更,通過節(jié)點(diǎn)間的通信,所有節(jié)點(diǎn)會(huì)很快得知整個(gè)集群的狀態(tài),使集群收斂。

          節(jié)點(diǎn)間發(fā)送的消息主要分為 5 種:meet 消息ping 消息pong 消息fail 消息publish 消息。不同的消息類型,通信協(xié)議、發(fā)送的頻率和時(shí)機(jī)、接收節(jié)點(diǎn)的選擇等是不同的:

          1055bbc0526d9911449fdac652cb6264.webp
          • MEET 消息: 用于通知新節(jié)點(diǎn)加入。消息發(fā)送者通知接收者加入到當(dāng)前集群,meet 消息通信正常完成后,接收節(jié)點(diǎn)會(huì)加入到集群中并進(jìn)行周期性的 ping、pong 消息交換。
          • PING 消息: 集群里每個(gè)節(jié)點(diǎn)每秒鐘會(huì)選擇部分節(jié)點(diǎn)發(fā)送 PING 消息,接收者收到消息后會(huì)回復(fù)一個(gè) PONG 消息。PING 消息的內(nèi)容是自身節(jié)點(diǎn)和部分其他節(jié)點(diǎn)的狀態(tài)信息,作用是彼此交換信息,以及檢測節(jié)點(diǎn)是否在線。PING 消息使用 Gossip 協(xié)議發(fā)送,接收節(jié)點(diǎn)的選擇兼顧了收斂速度和帶寬成本(內(nèi)部頻繁進(jìn)行信息交換,而且 ping/pong 消息會(huì)攜帶當(dāng)前節(jié)點(diǎn)和部分其他節(jié)點(diǎn)的狀態(tài)數(shù)據(jù),勢必會(huì)加重帶寬和計(jì)算的負(fù)擔(dān),所以選擇需要通信的節(jié)點(diǎn)列表就很重要了),具體規(guī)則如下
          1. 隨機(jī)找 5 個(gè)節(jié)點(diǎn),在其中選擇最久沒有通信的 1 個(gè)節(jié)點(diǎn);
          2. 掃描節(jié)點(diǎn)列表,選擇最近一次收到 PONG 消息時(shí)間大于 cluster_node_timeout / 2 的所有節(jié)點(diǎn),防止這些節(jié)點(diǎn)長時(shí)間未更新。
          1bfc5cfaa0e2f23991e840b9dbc2fdc5.webp
          • PONG消息: PONG 消息封裝了自身狀態(tài)數(shù)據(jù)。可以分為兩種:
          1. 第一種 是在接到 MEET/PING 消息后回復(fù)的 PONG 消息;
          2. 第二種 是指節(jié)點(diǎn)向集群廣播 PONG 消息,這樣其他節(jié)點(diǎn)可以獲知該節(jié)點(diǎn)的最新信息,例如故障恢復(fù)后新的主節(jié)點(diǎn)會(huì)廣播 PONG 消息。
          FAIL 消息: 當(dāng)節(jié)點(diǎn)判定集群內(nèi)另一個(gè)節(jié)點(diǎn)下線時(shí),會(huì)向集群內(nèi)廣播一個(gè) fail 消息,其他節(jié)點(diǎn)接收到 fail 消息之后把對應(yīng)節(jié)點(diǎn)更新為下線狀態(tài)。PUBLISH 消息: 節(jié)點(diǎn)收到 PUBLISH 命令后,會(huì)先執(zhí)行該命令,然后向集群廣播這一消息,接收節(jié)點(diǎn)也會(huì)執(zhí)行該 PUBLISH 命令。

          消息結(jié)構(gòu)

          所有的消息格式劃分為:消息頭消息體。消息頭包含發(fā)送節(jié)點(diǎn)自身狀態(tài)數(shù)據(jù),接收節(jié)點(diǎn)根據(jù)消息頭就可以獲取到發(fā)送節(jié)點(diǎn)的相關(guān)數(shù)據(jù),結(jié)構(gòu)如下( src/cluster.h 目錄下可以大概看下源碼):

          typedef?struct?{
          ????char?sig[4];????????/*?信號標(biāo)示?*/
          ????uint32_t?totlen;????/*?消息總長度?*/
          ????uint16_t?ver;???????/*?協(xié)議版本?*/
          ????uint16_t?port;??????/*?TCP?base?port?number.?*/
          ????uint16_t?type;??????/*?消息類型?*/
          ????uint16_t?count;?????/*?Only?used?for?some?kind?of?messages.?*/
          ????uint64_t?currentEpoch;??/*?當(dāng)前發(fā)送節(jié)點(diǎn)的配置紀(jì)元?*/
          ????uint64_t?configEpoch;???/*?主節(jié)點(diǎn)/從節(jié)點(diǎn)的主節(jié)點(diǎn)配置紀(jì)元?*/
          ????uint64_t?offset;????/*?復(fù)制偏移量?*/
          ????char?sender[CLUSTER_NAMELEN];?/*?Name?of?the?sender?node?*/
          ????unsigned?char?myslots[CLUSTER_SLOTS/8];
          ????char?slaveof[CLUSTER_NAMELEN];
          ????char?myip[NET_IP_STR_LEN];????/*?Sender?IP,?if?not?all?zeroed.?*/
          ????char?notused1[34];??/*?34?bytes?reserved?for?future?usage.?*/
          ????uint16_t?cport;??????/*?Sender?TCP?cluster?bus?port?*/
          ????uint16_t?flags;??????/*?發(fā)送節(jié)點(diǎn)標(biāo)識,區(qū)分主從角色,是否下線等?*/
          ????unsigned?char?state;?/*?發(fā)送節(jié)點(diǎn)所處的集群狀態(tài)?*/
          ????unsigned?char?mflags[3];?/*?消息標(biāo)識:?CLUSTERMSG_FLAG[012]_...?*/
          ????union?clusterMsgData?data;?/*?消息正文?*/
          }?clusterMsg;

          集群內(nèi)所有的消息都采用相同的消息頭結(jié)構(gòu) clusterMsg,它包含了發(fā)送節(jié)點(diǎn)關(guān)鍵信息,如節(jié)點(diǎn) id、槽映射、節(jié)點(diǎn)標(biāo)識(主從角色,是否下線)等。

          消息體在 Redis 內(nèi)部采用 clusterMsgData 結(jié)構(gòu)聲明,結(jié)構(gòu)如下:

          union?clusterMsgData?{
          ????/*?PING,?MEET?and?PONG?*/
          ????struct?{
          ????????/*?Array?of?N?clusterMsgDataGossip?structures?*/
          ????????clusterMsgDataGossip?gossip[1];
          ????}?ping;

          ????/*?FAIL?*/
          ????struct?{
          ????????clusterMsgDataFail?about;
          ????}?fail;

          ????/*?PUBLISH?*/
          ????struct?{
          ????????clusterMsgDataPublish?msg;
          ????}?publish;

          ????/*?UPDATE?*/
          ????struct?{
          ????????clusterMsgDataUpdate?nodecfg;
          ????}?update;

          ????/*?MODULE?*/
          ????struct?{
          ????????clusterMsgModule?msg;
          ????}?module;
          };

          消息體 clusterMsgData 定義發(fā)送消息的數(shù)據(jù),其中 ping、meet、pong 都采用 clusterMsgDataGossip 數(shù)組作為消息體數(shù)據(jù),實(shí)際消息類型使用消息頭的 type 屬性區(qū)分。每個(gè)消息體包含該節(jié)點(diǎn)的多個(gè) clusterMsgDataGossip 結(jié)構(gòu)數(shù)據(jù),用于信息交換,結(jié)構(gòu)如下:

          typedef?struct?{
          ????char?nodename[CLUSTER_NAMELEN];
          ????uint32_t?ping_sent;???????/*?最后一次向該節(jié)點(diǎn)發(fā)送ping消息時(shí)間?*/
          ????uint32_t?pong_received;??/*?最后一次接收該節(jié)點(diǎn)pong消息時(shí)間?*/
          ????char?ip[NET_IP_STR_LEN];??/*?IP?address?last?time?it?was?seen?*/
          ????uint16_t?port;??????????????/*?base?port?last?time?it?was?seen?*/
          ????uint16_t?cport;?????????????/*?cluster?port?last?time?it?was?seen?*/
          ????uint16_t?flags;?????????????/*?node->flags?copy?*/
          ????uint32_t?notused1;
          }?clusterMsgDataGossip;

          消息交互的過程就是解析消息頭和消息體的過程

          • 解析消息頭過程:消息頭包含了發(fā)送節(jié)點(diǎn)的信息,如果發(fā)送節(jié)點(diǎn)是新節(jié)點(diǎn)且消息是 meet 類型,則加入到本地節(jié)點(diǎn)列表;如果是已知節(jié)點(diǎn),則嘗試更新發(fā)送節(jié)點(diǎn)的狀態(tài),如槽映射關(guān)系、主從角色等狀態(tài)。
          • 解析消息體過程:如果消息體的 clusterMsgDataGossip 數(shù)組包含的節(jié)點(diǎn)是新節(jié)點(diǎn),則嘗試發(fā)起與新節(jié)點(diǎn)的 meet 握手流程;如果是已知節(jié)點(diǎn),則根據(jù) clusterMsgDataGossip 中的 flags 字段判斷該節(jié)點(diǎn)是否下線,用于故障轉(zhuǎn)移

          消息處理完后回復(fù) pong 消息,內(nèi)容同樣包含消息頭和消息體,發(fā)送節(jié)點(diǎn)接收到回復(fù)的 pong 消息后,采用類似的流程解析處理消息并更新與接收節(jié)點(diǎn)最后通信時(shí)間,完成一次消息通信。

          數(shù)據(jù)結(jié)構(gòu)

          節(jié)點(diǎn)需要專門的數(shù)據(jù)結(jié)構(gòu)來存儲(chǔ)集群的狀態(tài)。所謂集群的狀態(tài),是一個(gè)比較大的概念,包括:集群是否處于上線狀態(tài)、集群中有哪些節(jié)點(diǎn)、節(jié)點(diǎn)是否可達(dá)、節(jié)點(diǎn)的主從狀態(tài)、槽的分布……

          節(jié)點(diǎn)為了存儲(chǔ)集群狀態(tài)而提供的數(shù)據(jù)結(jié)構(gòu)中,最關(guān)鍵的是 clusterNodeclusterState 結(jié)構(gòu):前者記錄了一個(gè)節(jié)點(diǎn)的狀態(tài),后者記錄了集群作為一個(gè)整體的狀態(tài)。

          clusterNode 結(jié)構(gòu)保存了 一個(gè)節(jié)點(diǎn)的當(dāng)前狀態(tài),包括創(chuàng)建時(shí)間、節(jié)點(diǎn) id、ip 和端口號等。每個(gè)節(jié)點(diǎn)都會(huì)用一個(gè) clusterNode 結(jié)構(gòu)記錄自己的狀態(tài),并為集群內(nèi)所有其他節(jié)點(diǎn)都創(chuàng)建一個(gè) clusterNode 結(jié)構(gòu)來記錄節(jié)點(diǎn)狀態(tài)。

          下面列舉了 clusterNode 的部分字段,并說明了字段的含義和作用(src/cluster.h):

          typedef?struct?clusterNode?{
          ????//節(jié)點(diǎn)創(chuàng)建時(shí)間
          ????mstime_t?ctime;
          ????//節(jié)點(diǎn)id
          ????char?name[REDIS_CLUSTER_NAMELEN];
          ????//節(jié)點(diǎn)的ip和端口號
          ????char?ip[REDIS_IP_STR_LEN];
          ????int?port;
          ????//節(jié)點(diǎn)標(biāo)識:整型,每個(gè)bit都代表了不同狀態(tài),如節(jié)點(diǎn)的主從狀態(tài)、是否在線、是否在握手等
          ????int?flags;
          ????//配置紀(jì)元:故障轉(zhuǎn)移時(shí)起作用,類似于哨兵的配置紀(jì)元
          ????uint64_t?configEpoch;
          ????//槽在該節(jié)點(diǎn)中的分布:占用16384/8個(gè)字節(jié),16384個(gè)比特;每個(gè)比特對應(yīng)一個(gè)槽:比特值為1,則該比特對應(yīng)的槽在節(jié)點(diǎn)中;比特值為0,則該比特對應(yīng)的槽不在節(jié)點(diǎn)中
          ????unsigned?char?slots[16384/8];
          ????//節(jié)點(diǎn)中槽的數(shù)量
          ????int?numslots;
          ????…………
          }?clusterNode;

          除了上述字段,clusterNode 還包含節(jié)點(diǎn)連接、主從復(fù)制、故障發(fā)現(xiàn)和轉(zhuǎn)移需要的信息等。

          clusterState 結(jié)構(gòu)保存了在當(dāng)前節(jié)點(diǎn)視角下,集群所處的狀態(tài)。主要字段包括(src/cluster.h):

          typedef?struct?clusterState?{
          ????clusterNode?*myself;??//自身節(jié)點(diǎn)
          ????uint64_t?currentEpoch;????//配置紀(jì)元
          ????//集群狀態(tài):在線還是下線
          ????int?state;
          ????//集群中至少包含一個(gè)槽的節(jié)點(diǎn)數(shù)量
          ????int?size;
          ????//哈希表,節(jié)點(diǎn)名稱->clusterNode節(jié)點(diǎn)指針
          ????dict?*nodes;
          ????//槽分布信息:數(shù)組的每個(gè)元素都是一個(gè)指向clusterNode結(jié)構(gòu)的指針;如果槽還沒有分配給任何節(jié)點(diǎn),則為NULL
          ????clusterNode?*slots[16384];
          ????…………
          }?clusterState;

          除此之外,clusterState 還包括故障轉(zhuǎn)移、槽遷移等需要的信息。

          3.4 集群自動(dòng)故障轉(zhuǎn)移

          Redis 集群自身實(shí)現(xiàn)了高可用。高可用首先需要解決集群部分失敗的場景:當(dāng)集群內(nèi)少量節(jié)點(diǎn)出現(xiàn)故障時(shí)通過自動(dòng)故障轉(zhuǎn)移保證集群可以正常對外提供服務(wù)。

          之前我們了解了哨兵機(jī)制的故障發(fā)現(xiàn)和故障轉(zhuǎn)移。集群的實(shí)現(xiàn)有些思路類似。

          3.4.1 故障發(fā)現(xiàn)

          通過定時(shí)任務(wù)發(fā)送 PING 消息檢測其他節(jié)點(diǎn)狀態(tài);節(jié)點(diǎn)下線分為主觀下線客觀下線

          • 主觀下線:集群中每個(gè)節(jié)點(diǎn)都會(huì)定期向其他節(jié)點(diǎn)發(fā)送 ping 消息,接收節(jié)點(diǎn)回復(fù) pong 消息作為響應(yīng)。如果在 cluster-node-timeout 時(shí)間內(nèi)通信一直失敗,則發(fā)送節(jié)點(diǎn)會(huì)認(rèn)為接收節(jié)點(diǎn)存在故障,把接收節(jié)點(diǎn)標(biāo)記為主觀下線(pfail)狀態(tài)

          • 當(dāng)某個(gè)節(jié)點(diǎn)判斷另一個(gè)節(jié)點(diǎn)主觀下線后,相應(yīng)的節(jié)點(diǎn)狀態(tài)會(huì)跟隨消息在集群內(nèi)傳播。ping/pong 消息的消息體會(huì)攜帶集群 1/10 的其他節(jié)點(diǎn)狀態(tài)數(shù)據(jù), 當(dāng)接受節(jié)點(diǎn)發(fā)現(xiàn)消息體中含有主觀下線的節(jié)點(diǎn)狀態(tài)時(shí),會(huì)在本地找到故障節(jié)點(diǎn)的 ClusterNode 結(jié)構(gòu),保存到下線報(bào)告鏈表中。結(jié)構(gòu)如下:

            struct?clusterNode?{????/*?認(rèn)為是主觀下線的clusterNode結(jié)構(gòu)?*/?
            ??list?*fail_reports;?/*?記錄了所有其他節(jié)點(diǎn)對該節(jié)點(diǎn)的下線報(bào)告?*/?...
            };

            通過 Gossip 消息傳播,集群內(nèi)節(jié)點(diǎn)不斷收集到故障節(jié)點(diǎn)的下線報(bào)告。當(dāng)半數(shù)以上持有槽的主節(jié)點(diǎn)都標(biāo)記某個(gè)節(jié)點(diǎn)是主觀下線時(shí)。觸發(fā)客觀下線流程。

            這里有兩個(gè)問題:

          1. 為什么必須是負(fù)責(zé)槽的主節(jié)點(diǎn)參與故障發(fā)現(xiàn)決策?

            因?yàn)榧耗J较?只有處理槽的主節(jié)點(diǎn)才負(fù)責(zé)讀寫請求和集群槽等關(guān)鍵信息維護(hù),而從節(jié)點(diǎn)只進(jìn)行主節(jié)點(diǎn)數(shù)據(jù)和狀態(tài)信息的復(fù)制。

          2. 為什么半數(shù)以上處理槽的主節(jié)點(diǎn)?

            必須半數(shù)以上是為了應(yīng)對網(wǎng)絡(luò)分區(qū)等原因造成的集群分割情況,被分割的小集群因?yàn)闊o法完成從主觀下線到 客觀下線這一關(guān)鍵過程,從而防止小集群完成故障轉(zhuǎn)移之后繼續(xù)對外提供服務(wù)。

          3.4.2 故障恢復(fù)

          故障節(jié)點(diǎn)變?yōu)榭陀^下線后,如果下線節(jié)點(diǎn)是持有槽的主節(jié)點(diǎn)則需要在它的從節(jié)點(diǎn)中選出一個(gè)替換它,從而保證集群的高可用。

          下線主節(jié)點(diǎn)的所有從節(jié)點(diǎn)承擔(dān)故障恢復(fù)的義務(wù),當(dāng)從節(jié)點(diǎn)通過內(nèi)部定時(shí)任務(wù)發(fā)現(xiàn)自身復(fù)制的主節(jié)點(diǎn)進(jìn)入客觀下線時(shí),將會(huì)觸發(fā)故障恢復(fù)流程:

          1. 資格檢查

            每個(gè)從節(jié)點(diǎn)都要檢查最后與主節(jié)點(diǎn)斷線時(shí)間,判斷是否有資格替換故障的主節(jié)點(diǎn)。如果從節(jié)點(diǎn)與主節(jié)點(diǎn)斷線時(shí)間超過 cluster-node-time*cluster-slave-validity-factor,則當(dāng)前從節(jié)點(diǎn)不具備故障轉(zhuǎn)移資格。參數(shù) cluster-slave- validity-factor 用于從節(jié)點(diǎn)的有效因子,默認(rèn)為 10。

          2. 準(zhǔn)備選舉時(shí)間

            當(dāng)從節(jié)點(diǎn)符合故障轉(zhuǎn)移資格后,更新觸發(fā)故障選舉的時(shí)間,只有到達(dá)該時(shí)間后才能執(zhí)行后續(xù)流程

            struct?clusterState?{?
            ??...
            ?mstime_t?failover_auth_time;?/*?記錄之前或者下次將要執(zhí)行故障選舉時(shí)間?*/
            ?int?failover_auth_rank;?/*?記錄當(dāng)前從節(jié)點(diǎn)排名?*/?}

            這里之所以采用延遲觸發(fā)機(jī)制,主要是通過對多個(gè)從節(jié)點(diǎn)使用不同的延遲選舉時(shí)間來支持優(yōu)先級問題。復(fù)制偏移量越大說明從節(jié)點(diǎn)延遲越低,那么它應(yīng)該具有更高的優(yōu)先級來替換故障主節(jié)點(diǎn)。

          3. 發(fā)起選舉

            當(dāng)從節(jié)點(diǎn)定時(shí)任務(wù)檢測到達(dá)故障選舉時(shí)間(failover_auth_time)到達(dá)后,發(fā)起選舉流程如下:

          • 更新配置紀(jì)元

            配置紀(jì)元是一個(gè)只增不減的整數(shù),每個(gè)主節(jié)點(diǎn)自身維護(hù)一個(gè)配置紀(jì)元 (clusterNode.configEpoch)標(biāo)示當(dāng)前主節(jié)點(diǎn)的版本,所有主節(jié)點(diǎn)的配置紀(jì)元 都不相等,從節(jié)點(diǎn)會(huì)復(fù)制主節(jié)點(diǎn)的配置紀(jì)元。整個(gè)集群又維護(hù)一個(gè)全局的配 置紀(jì)元(clusterState.current Epoch),用于記錄集群內(nèi)所有主節(jié)點(diǎn)配置紀(jì)元的最大版本。

          • 廣播選舉消息

          選舉投票

          只有持有槽的主節(jié)點(diǎn)才會(huì)處理故障選舉消息 (FAILOVER_AUTH_REQUEST),因?yàn)槊總€(gè)持有槽的節(jié)點(diǎn)在一個(gè)配置紀(jì)元內(nèi)都有唯一的一張選票,當(dāng)接到第一個(gè)請求投票的從節(jié)點(diǎn)消息時(shí)回復(fù) FAILOVER_AUTH_ACK 消息作為投票,之后相同配置紀(jì)元內(nèi)其他從節(jié)點(diǎn)的選舉消息將忽略。

          投票過程其實(shí)是一個(gè)領(lǐng)導(dǎo)者選舉的過程,如集群內(nèi)有 N 個(gè)持有槽的主節(jié)點(diǎn)代表有 N 張選票。由于在每個(gè)配置紀(jì)元內(nèi)持有槽的主節(jié)點(diǎn)只能投票給一個(gè) 從節(jié)點(diǎn),因此只能有一個(gè)從節(jié)點(diǎn)獲得N/2+1的選票,保證能夠找出唯一的從節(jié)點(diǎn)。

          Redis 集群沒有直接使用從節(jié)點(diǎn)進(jìn)行領(lǐng)導(dǎo)者選舉,主要因?yàn)閺墓?jié)點(diǎn)數(shù)必須大于等于 3 個(gè)才能保證湊夠 N/2+1 個(gè)節(jié)點(diǎn),將導(dǎo)致從節(jié)點(diǎn)資源浪費(fèi)。使用集群內(nèi)所有持有槽的主節(jié)點(diǎn)進(jìn)行領(lǐng)導(dǎo)者選舉,即使只有一個(gè)從節(jié)點(diǎn)也可以完成選舉過程。

          當(dāng)從節(jié)點(diǎn)收集到 N/2+1 個(gè)持有槽的主節(jié)點(diǎn)投票時(shí),從節(jié)點(diǎn)可以執(zhí)行替換主節(jié)點(diǎn)操作,例如集群內(nèi)有 5 個(gè)持有槽的主節(jié)點(diǎn),主節(jié)點(diǎn) b 故障后還有 4 個(gè), 當(dāng)其中一個(gè)從節(jié)點(diǎn)收集到 3 張投票時(shí)代表獲得了足夠的選票可以進(jìn)行替換主節(jié)點(diǎn)操作。

          ce6ad0da11b18a7330feb9f39d02c88b.webp

          1. 替換主節(jié)點(diǎn)

            當(dāng)從節(jié)點(diǎn)收集到足夠的選票之后,觸發(fā)替換主節(jié)點(diǎn)操作:

          • 當(dāng)前從節(jié)點(diǎn)取消復(fù)制變?yōu)橹鞴?jié)點(diǎn)。

          • 執(zhí)行 clusterDelSlot 操作撤銷故障主節(jié)點(diǎn)負(fù)責(zé)的槽,并執(zhí)行 clusterAddSlot 把這些槽委派給自己。

          • 向集群廣播自己的 pong 消息,通知集群內(nèi)所有的節(jié)點(diǎn)當(dāng)前從節(jié)點(diǎn)變?yōu)橹鞴?jié)點(diǎn)并接管了故障主節(jié)點(diǎn)的槽信息。

          與哨兵一樣,集群只實(shí)現(xiàn)了主節(jié)點(diǎn)的故障轉(zhuǎn)移;從節(jié)點(diǎn)故障時(shí)只會(huì)被下線,不會(huì)進(jìn)行故障轉(zhuǎn)移。因此,使用集群時(shí),應(yīng)謹(jǐn)慎使用讀寫分離技術(shù),因?yàn)閺墓?jié)點(diǎn)故障會(huì)導(dǎo)致讀服務(wù)不可用,可用性變差。

          3.5 客戶端訪問集群

          3.5.1 redis-cli

          當(dāng)節(jié)點(diǎn)收到 redis-cli 發(fā)來的命令(如 set/get )時(shí),過程如下:

          1. 計(jì)算 key 屬于哪個(gè)槽:CRC16(key) & 16383

            集群提供的 cluster keyslot 命令也是使用上述公式實(shí)現(xiàn)

            127.0.0.1:7001>?cluster?keyslot?k1
            (integer)?12706
          2. 判斷 key 所在的槽是否在當(dāng)前節(jié)點(diǎn)

            假設(shè) key 位于第 i 個(gè)槽,clusterState.slots[i] 則指向了槽所在的節(jié)點(diǎn),如果 clusterState.slots[i]==clusterState.myself,說明槽在當(dāng)前節(jié)點(diǎn),可以直接在當(dāng)前節(jié)點(diǎn)執(zhí)行命令;否則,說明槽不在當(dāng)前節(jié)點(diǎn),則查詢槽所在節(jié)點(diǎn)的地址(clusterState.slots[i].ip/port),并將其包裝到 MOVED 錯(cuò)誤中返回給 redis-cli。

          3. redis-cli 收到 MOVED 錯(cuò)誤后,根據(jù)返回的 ip 和 port 重新發(fā)送請求

          像 redis-cli 這種客戶端又叫 Dummy(傀儡)客戶端,它優(yōu)點(diǎn)是代碼實(shí)現(xiàn)簡單,對客戶端協(xié)議影響較小,只需要根據(jù)重定向信息再次發(fā)送請求即可。但是它的弊端很明顯,每次執(zhí)行鍵命令前都要到 Redis 上進(jìn)行重定向才能找到要執(zhí)行命令的節(jié)點(diǎn),額外增加了 IO 開銷, 這不是Redis 集群高效的使用方式。正因?yàn)槿绱送ǔ<嚎蛻舳硕疾捎昧硪?種實(shí)現(xiàn):Smart(智能)客戶端。

          3.5.2 Smart客戶端

          大多數(shù)開發(fā)語言的 Redis 客戶端都采用 Smart 客戶端支持集群協(xié)議。Smart 客戶端通過在內(nèi)部維護(hù) slot→node 的映射關(guān)系,本地就可實(shí)現(xiàn)鍵到節(jié)點(diǎn)的查找,從而保證 IO 效率的最大化,而 MOVED 重定向負(fù)責(zé)協(xié)助 Smart 客戶端更新 slot→node 映射。

          以 Jedis 為例,說明 Smart 客戶端操作集 群的流程:

          1. 首先在 JedisCluster 初始化時(shí)會(huì)選擇一個(gè)運(yùn)行節(jié)點(diǎn),初始化槽和節(jié)點(diǎn)映射關(guān)系,使用 cluster slots 命令完成

            127.0.0.1:7001>?cluster?slots
            1)?1)?(integer)?10923???//?開始槽范圍
            ???2)?(integer)?16383??//?結(jié)束槽范圍
            ???3)?1)?"127.0.0.1"????//主節(jié)點(diǎn)ip
            ??????2)?(integer)?7002?//從節(jié)點(diǎn)端口
            ??????3)?"911f517c6cc3501d42b9bba4aa58b5633375abb5"
            ???4)?1)?"127.0.0.1"
            ??????2)?(integer)?7004
            ??????3)?"897b5abb7f79117bb645d79671ced5bebcb855ad"
            2)?1)?(integer)?5461
            ???2)?(integer)?10922
            ???3)?1)?"127.0.0.1"
            ??????2)?(integer)?7001
            ??????3)?"f130468fc299509d709b0867b111342576f00b23"
            ???4)?1)?"127.0.0.1"
            ??????2)?(integer)?7003
            ??????3)?"cc122bc7aa0d36da44a5d8c7fbe061940aa81a1d"
            127.0.0.1:7001>?
          2. JedisCluster 解析 cluster slots 結(jié)果緩存在本地,并為每個(gè)節(jié)點(diǎn)創(chuàng)建唯一的 JedisPool 連接池。映射關(guān)系在 JedisClusterInfoCache類中

          3. 當(dāng)執(zhí)行命令時(shí),JedisCluster 根據(jù) key->slot->node 選擇需要連接的節(jié)點(diǎn),發(fā)送命令。如果成功,則命令執(zhí)行完畢。如果執(zhí)行失敗,則會(huì)隨機(jī)選擇其他節(jié)點(diǎn)進(jìn)行重試,并在出現(xiàn) MOVED 錯(cuò)誤時(shí),使用 cluster slots 重新同步 slot->node 的映射關(guān)系

          參考與來源

          1. https://redis.io/topics/cluster-tutorial
          2. 《Redis 設(shè)計(jì)與實(shí)現(xiàn)》
          3. 《Redis 開發(fā)與運(yùn)維》
          4. https://www.cnblogs.com/kismetv/p/9853040.html
          422ab3963235aad6ab4b84084cfe149d.webp6f310cdd5504b1e98eef7654acd610ba.webp

          Redis 哨兵模式



          瀏覽 93
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  影音先锋在线观看资源男人网 | 成人福利视频网站 | 在线看片黄色免费Z | 亚洲精品成a在线 | 亚洲色情免费 |