九、redis集群


title: redis集群
date: 2021-01-14 20:12:14
categories: redis
tags:
- redis cluster
description: 集群模式是redis的分布式解決方案


redis集群數(shù)據(jù)分區(qū)

一個(gè)集群必須要解決數(shù)據(jù)分布的問(wèn)題,常用的方法有哈希分區(qū)、順序分區(qū)。哈希分區(qū)的特點(diǎn)是離散度好數(shù)據(jù),而順序分區(qū)則可以提供順序訪問(wèn)。

redis采用虛擬槽分區(qū)的方式來(lái)將數(shù)據(jù)分區(qū),一共16384個(gè)槽,所有的鍵根據(jù)哈希函數(shù)slog=CRC16(key)&16383,映射到0~16383整數(shù)槽內(nèi)。
每一個(gè)節(jié)點(diǎn)負(fù)責(zé)維護(hù)一部分槽,以及槽所對(duì)應(yīng)的鍵值數(shù)據(jù)

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

  • 解耦數(shù)據(jù)與節(jié)點(diǎn)之間的關(guān)系
  • 節(jié)點(diǎn)自身維護(hù)槽的映射關(guān)系,不需要客戶端或者代理服務(wù)維護(hù)槽分區(qū)元數(shù)據(jù)
  • 支持節(jié)點(diǎn)、槽、鍵之間的映射查詢,用于數(shù)據(jù)路由、在線伸縮等場(chǎng)景

redis集群功能缺陷:

  1. key批量操作支持有限,只能支持具有相同slot值的key執(zhí)行批量操作
  2. 同理事物也同樣只支持同一節(jié)點(diǎn)上的事物操作
  3. key作為數(shù)據(jù)分區(qū)的最小粒度,因此不能將一個(gè)大的鍵值對(duì)象如hash、list等拆分到不同的節(jié)點(diǎn)分布
  4. 不支持多數(shù)據(jù)庫(kù),只有一個(gè)db0
  5. 復(fù)制結(jié)構(gòu)只支持一層、從節(jié)點(diǎn)只能復(fù)制主節(jié)點(diǎn),不支持樹狀分布

集群搭建

  1. 準(zhǔn)備好6個(gè)節(jié)點(diǎn):

    redis-6379.conf
    redis-6380.conf
    redis-6381.conf
    redis-6382.conf
    redis-6383.conf
    redis-6384.conf
    $> cat redis-6379.conf
    port 6379
    cluster-enabled yes
    cluster-node-timeout 15000
    # 如果沒(méi)有配置,則會(huì)在啟動(dòng)后生成一份
    cluster-config-file "nodes-6379.conf"
    logfile "/redis/logs/redis-6379.log"
    dbfilename "dump-6379.rdb"
    
  2. 啟動(dòng)集群
    redis-server conf/redis-6379.conf
    目前每個(gè)節(jié)點(diǎn)都只能識(shí)別自己的節(jié)點(diǎn)新,因?yàn)楝F(xiàn)在彼此不知道對(duì)方的存在

    127.0.0.1:6379> CLUSTER NODES
    6e39c9aba1bb22c51e1fb6d37aeda4174a782eac :6379@16379 myself,master - 0 0 0 connected
    

手動(dòng)建立集群

  1. 節(jié)點(diǎn)握手
    節(jié)點(diǎn)握手是讓當(dāng)前節(jié)點(diǎn)感知到另一個(gè)節(jié)點(diǎn),由客戶端發(fā)起命令,cluster meet {ip} {port}


    節(jié)點(diǎn)握手

節(jié)點(diǎn)握手后集群還不能正常工作,因?yàn)檫€沒(méi)有分配槽,這個(gè)時(shí)候?qū)懨顣?huì)發(fā)現(xiàn)失敗:

```sh
127.0.0.1:6379> set hello redis
(error) CLUSTERDOWN Hash slot not served
# 查看集群信息,發(fā)現(xiàn)state是fail狀態(tài),分配的槽slot為0
127.0.0.1:6379> cluster info
cluster_state:fail
cluster_slots_assigned:0
cluster_slots_ok:0
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:0
cluster_current_epoch:5
cluster_my_epoch:2
cluster_stats_messages_ping_sent:490
cluster_stats_messages_pong_sent:210
cluster_stats_messages_meet_sent:7
cluster_stats_messages_sent:707
cluster_stats_messages_ping_received:210
cluster_stats_messages_pong_received:198
cluster_stats_messages_received:408

```
  1. 分配槽
    之前說(shuō)過(guò),redis集群把所有的數(shù)據(jù)映射到16384個(gè)槽中,而這些槽如何分配,是要通過(guò)配置來(lái)決定。只有這些槽都被分配好了
    redis集群才能響應(yīng)命令,通過(guò)cluster addslots命令為節(jié)點(diǎn)分槽。(正常做法應(yīng)該使用create命令)

    ./redis-cli 9 -p 6381 cluster addslots {0..5461}
    ./redis-cli 9 -p 6381 cluster addslots {5462..10922}
    ./redis-cli 9 -p 6381 cluster addslots {10923..16383}
    

此時(shí)集群已經(jīng)可用了,為了集群的高可用,我們?cè)贋槊恳粋€(gè)節(jié)點(diǎn)分配一個(gè)從節(jié)點(diǎn)。

  1. 配置集群從節(jié)點(diǎn) cluster replicate {id}
    CLUSTER REPLICATE 6e39c9aba1bb22c51e1fb6d37aeda4174a782eac注意后面跟的是clusterid
    以上步驟依照Redis協(xié)議手動(dòng)建立了一個(gè)集群,便于理解集群建立的過(guò)程,但比較繁瑣且容易出錯(cuò),因此,官方自帶工具方便我們快速搭建集群,之前我們使用
    redis-trib.rb來(lái)創(chuàng)建,在Redis5.0中創(chuàng)建集群已經(jīng)使用“redis-cli”來(lái)實(shí)現(xiàn),所以redis-trib.rb的方式已經(jīng)被拋棄

利用cluster create快速搭建集群

和上面的步驟一樣先配置六個(gè)不同的配置文件,再啟動(dòng)6個(gè)進(jìn)程,不過(guò)節(jié)點(diǎn)握手和分配槽這個(gè)步驟不一樣,我們是有redis-cli來(lái)簡(jiǎn)化操作

redis-cli --cluster subcommand [args] [opt]
./redis-cli --cluster create --cluster-replicas 1 127.0.0.1:6380 127.0.0.1:6379 127.0.0.1:6381 127.0.0.1:6382 127.0.0.1:6383 127.0.0.1:6384

最后輸出結(jié)果

Master[0] -> Slots 0 - 5460
Master[1] -> Slots 5461 - 10922
Master[2] -> Slots 10923 - 16383
Adding replica 127.0.0.1:6383 to 127.0.0.1:6380
Adding replica 127.0.0.1:6384 to 127.0.0.1:6379
Adding replica 127.0.0.1:6382 to 127.0.0.1:6381
# 優(yōu)化
>>> Trying to optimize slaves allocation for anti-affinity
[WARNING] Some slaves are in the same host as their master
M: 6e2da8e45d804b47fe599d121682e6a394c527ef 127.0.0.1:6380
   slots:[0-5460] (5461 slots) master
M: 4aa937577181f82ce88b1c909a519ca03963ab21 127.0.0.1:6379
   slots:[5461-10922] (5462 slots) master
M: 07b085e9e9750e5d0c319f3c099d70acda84f6fb 127.0.0.1:6381
   slots:[10923-16383] (5461 slots) master
S: 0c1c1ce2bf3c890396da31a1b536adcd32e2766a 127.0.0.1:6382
   replicates 07b085e9e9750e5d0c319f3c099d70acda84f6fb
S: 36b71d82eaccae49f0aff395fa3cae03c1adcc0f 127.0.0.1:6383
   replicates 6e2da8e45d804b47fe599d121682e6a394c527ef
S: 4885dcbd061e28d969eb8d2ae893f256c9c79401 127.0.0.1:6384
   replicates 4aa937577181f82ce88b1c909a519ca03963ab21
# 確認(rèn)部署
Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join
# 其他校驗(yàn)信息
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered

可以看到:

  1. redis-cli命令會(huì)對(duì)節(jié)點(diǎn)分配進(jìn)行優(yōu)化,比如主從使用不同的ip等
  2. 部署方案需要人工再確認(rèn)一遍
  3. 會(huì)確保所有槽都被分配成功

集群內(nèi)通信

redis集群采用p2p的Gossip協(xié)議,類似留言,通過(guò)彼此不斷地通信交換信息,最后所有節(jié)點(diǎn)直到集群完整的信息。


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

特定規(guī)則是什么?

由于內(nèi)部需要頻繁的進(jìn)行節(jié)點(diǎn)信息交換,ping/pong消息會(huì)攜帶當(dāng)前節(jié)點(diǎn)和部分其他節(jié)點(diǎn)的狀態(tài)數(shù)據(jù),Redis集群內(nèi)
節(jié)點(diǎn)每秒執(zhí)行10次,因此節(jié)點(diǎn)每次選擇通信的節(jié)點(diǎn)列表非常重要。太多會(huì)影響帶寬,太少又不能做到及時(shí)交換信息。因此redis采用過(guò)程如下:
[圖片上傳失敗...(image-eae61e-1611843829725)]

消息的數(shù)據(jù)量

  • 消息頭:固定占用myslots[CLUSTER_SLOTS/8],2kb
  • 消息體:默認(rèn)是1/10個(gè)其他節(jié)點(diǎn)的信息,最少3個(gè),醉倒total-2個(gè),所以消息的大小跟節(jié)點(diǎn)數(shù)有關(guān)。

集群伸縮

集群伸縮就是利用槽在節(jié)點(diǎn)間的移動(dòng),也就是數(shù)據(jù)在各個(gè)節(jié)點(diǎn)的移動(dòng)。

擴(kuò)容

  1. 準(zhǔn)備節(jié)點(diǎn)

    > cp redis-6384.conf redis-6385.conf
    > sed -i 's/6384/6385/' redis-6385.conf
    > ./redis/src/redis-server config/redis-6385.conf
    
  2. 加入集群

    > ./redis/src/redis-cli --cluster add-node localhost:6385 localhost:6384
    >>> Adding node 127.0.0.1:6385 to cluster 127.0.0.1:6384
    >>> Performing Cluster Check (using node 127.0.0.1:6384)
    S: 4885dcbd061e28d969eb8d2ae893f256c9c79401 127.0.0.1:6384
       slots: (0 slots) slave
       replicates 4aa937577181f82ce88b1c909a519ca03963ab21
    M: ce5e60a517c8714d52b82d9e24b71d2f5a1868c4 127.0.0.1:6385
       slots: (0 slots) master
    S: 0c1c1ce2bf3c890396da31a1b536adcd32e2766a 127.0.0.1:6382
       slots: (0 slots) slave
       replicates 07b085e9e9750e5d0c319f3c099d70acda84f6fb
    M: 07b085e9e9750e5d0c319f3c099d70acda84f6fb 127.0.0.1:6381
       slots:[10923-16383] (5461 slots) master
       1 additional replica(s)
    S: 36b71d82eaccae49f0aff395fa3cae03c1adcc0f 127.0.0.1:6383
       slots: (0 slots) slave
       replicates 6e2da8e45d804b47fe599d121682e6a394c527ef
    M: 6e2da8e45d804b47fe599d121682e6a394c527ef 127.0.0.1:6380
       slots:[0-5460] (5461 slots) master
       1 additional replica(s)
    M: 4aa937577181f82ce88b1c909a519ca03963ab21 127.0.0.1:6379
       slots:[5461-10922] (5462 slots) master
       1 additional replica(s)
    [OK] All nodes agree about slots configuration.
    >>> Check for open slots...
    >>> Check slots coverage...
    [OK] All 16384 slots covered.
    # meet 節(jié)點(diǎn)
    >>> Send CLUSTER MEET to node 127.0.0.1:6385 to make it join the cluster.
    [OK] New node added correctly
    
    # 檢查node情況,發(fā)現(xiàn)已經(jīng)加入
    > cluster nodes
    4aa937577181f82ce88b1c909a519ca03963ab21 127.0.0.1:6379@16379 myself,master - 0 1611108761000 2 connected 5461-10922
    36b71d82eaccae49f0aff395fa3cae03c1adcc0f 127.0.0.1:6383@16383 slave 6e2da8e45d804b47fe599d121682e6a394c527ef 0 1611108764049 5 connected
    07b085e9e9750e5d0c319f3c099d70acda84f6fb 127.0.0.1:6381@16381 master - 0 1611108763000 3 connected 10923-16383
    ce5e60a517c8714d52b82d9e24b71d2f5a1868c4 127.0.0.1:6385@16385 master - 0 1611108763046 0 connected
    0c1c1ce2bf3c890396da31a1b536adcd32e2766a 127.0.0.1:6382@16382 slave 07b085e9e9750e5d0c319f3c099d70acda84f6fb 0 1611108762000 4 connected
    6e2da8e45d804b47fe599d121682e6a394c527ef 127.0.0.1:6380@16380 master - 0 1611108765050 1 connected 0-5460
    4885dcbd061e28d969eb8d2ae893f256c9c79401 127.0.0.1:6384@16384 slave 4aa937577181f82ce88b1c909a519ca03963ab21 0 1611108763000 6 connected
    
  3. 遷移槽
    繼續(xù)使用redis-cli cluster 命令,使用reshard參數(shù)遷移槽

    > ./redis/src/redis-cli --cluster reshard 127.0.0.1:6379
    
    # 進(jìn)行一系列校驗(yàn)后提示輸入要遷移的槽的個(gè)數(shù),輸入4096
    How many slots do you want to move (from 1 to 16384) 4069
    
    # 接著會(huì)提示輸入目標(biāo)節(jié)點(diǎn),也就是新加入的節(jié)點(diǎn)id
    what is the receiveing node ID ? ce5e60a517c8714d52b82d9e24b71d2f5a1868c4
    
    # 然后會(huì)提示輸入源節(jié)點(diǎn)的ID,也就是說(shuō)可以只轉(zhuǎn)移部分節(jié)點(diǎn)的槽  
    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: 24b9eea3f3797a68e33100dccfdbfc57693f7a4b
    Source node #2: 07b085e9e9750e5d0c319f3c099d70acda84f6fb
    Source node #3: 6e2da8e45d804b47fe599d121682e6a394c527ef
    # redis會(huì)打印每個(gè)槽的遷移計(jì)劃,會(huì)再確認(rèn)一次,確認(rèn)后會(huì)打印遷移過(guò)程,最后自動(dòng)退出
    

    從redis的輸出就可以看出,redis的遷移過(guò)程是一個(gè)一個(gè)槽遷移的,槽遷移過(guò)程如下:

    槽遷移過(guò)程

  4. 檢查槽的均衡性

    > ./redis/src/redis-cli --cluster rebalance 127.0.0.1:6379
    >>> Performing Cluster Check (using node 127.0.0.1:6379)
    [OK] All nodes agree about slots configuration.
    >>> Check for open slots...
    >>> Check slots coverage...
    [OK] All 16384 slots covered.
    *** No rebalancing needed! All nodes are within the 2.00% threshold.
    

    說(shuō)明遷移之后主節(jié)點(diǎn)負(fù)責(zé)的槽數(shù)量差異在2%以內(nèi),相對(duì)均勻,無(wú)需調(diào)整

  5. 添加從節(jié)點(diǎn)
    準(zhǔn)備一個(gè)6386節(jié)點(diǎn),步驟與前面一樣,然后在該節(jié)點(diǎn)上執(zhí)行 cluster replicate {masterId},注意集群模式下沒(méi)有salveof命令

集群收縮

原理和方法和擴(kuò)容是一樣的,都是重新分片reshard,只不過(guò)之前是把已有節(jié)點(diǎn)的數(shù)據(jù)遷移一部分到新節(jié)點(diǎn)。而收縮則是把要下線節(jié)點(diǎn)的數(shù)據(jù)遷移到其他節(jié)點(diǎn)。
執(zhí)行方法與擴(kuò)容一樣,都是用reshard。 注意del-node是刪除空節(jié)點(diǎn)的命令,如果有槽是不能刪除的。

下線后,為了讓其他節(jié)點(diǎn)忘記下線節(jié)點(diǎn),也就是避免繼續(xù)和下線節(jié)點(diǎn)交換信息,這個(gè)時(shí)候可以用del-node 命令。

請(qǐng)求路由

在集群模式下,Redis接受任何鍵相關(guān)的命令都要先計(jì)算鍵對(duì)應(yīng)的槽(每個(gè)集群節(jié)點(diǎn)在交換信息時(shí)都會(huì)發(fā)送槽信息,因此每個(gè)節(jié)點(diǎn)都知道所有槽和節(jié)點(diǎn)的對(duì)應(yīng)關(guān)系),再根據(jù)槽找出相應(yīng)的節(jié)點(diǎn),如果節(jié)點(diǎn)是自身,則處理命令,如果不是則恢復(fù)MOVED重定向錯(cuò)誤。通知客戶端請(qǐng)求正確的節(jié)點(diǎn)

127.0.0.1:6386> set key:test:1 value-1
(error) MOVED 5191 127.0.0.1:6380

使用redis-cli工具時(shí),可以加入-c參數(shù)支持自動(dòng)重定向,也就是不用手動(dòng)切換節(jié)點(diǎn)。原理是redis-cli客戶端再收到MOVED信息時(shí),再次發(fā)起請(qǐng)求。
redis節(jié)點(diǎn)對(duì)于不屬于它的鍵命令,只會(huì)回復(fù)重定向響應(yīng),并不負(fù)責(zé)轉(zhuǎn)發(fā)。重定向的過(guò)程由客戶端實(shí)現(xiàn)

./redis/src/redis-cli -p 6386 -c
127.0.0.1:6386> set key:test:1 value-1
-> Redirected to slot [5191] located at 127.0.0.1:6380
OK

如果鍵內(nèi)容包含{}大括號(hào)字符,則計(jì)算槽的有效部分是括號(hào)內(nèi)的內(nèi)容,其他內(nèi)容不會(huì)被計(jì)算,如果不包含則所有內(nèi)容都會(huì)加入計(jì)算,利用這一點(diǎn)我們可以優(yōu)化批量數(shù)據(jù),比如哈希類型的,我們?nèi)绻褂胔get的時(shí)候,如果鍵列表不再同一個(gè)槽中會(huì)報(bào)錯(cuò),這個(gè)時(shí)候可以用hash_tag來(lái)使該哈希表中所有的鍵都在同一個(gè)槽中。

100.101.71.99:6380> CLUSTER KEYSLOT key:test:111
(integer) 10050
100.101.71.99:6380> CLUSTER KEYSLOT key:{test}:111
(integer) 6918
100.101.71.99:6380> CLUSTER KEYSLOT key:{test}:112
(integer) 6918
100.101.71.99:6380> CLUSTER KEYSLOT key:{test}:22
(integer) 6918
  • ASK重定向
    與MOVED重定向不一樣,ASK重定向,是發(fā)生在槽遷移過(guò)程中,一部分遷移完成,一部分沒(méi)有遷移成功,客戶端根據(jù)本地slots緩存發(fā)送命令到源節(jié)點(diǎn),但其實(shí)已經(jīng)發(fā)送到了目標(biāo)節(jié)點(diǎn),因此會(huì)回復(fù)(error)ASK {slot}{targetIP}:{targetIP}

Smart客戶端

顯然每次通過(guò)重定向來(lái)完成操作,會(huì)造成很大的IO浪費(fèi)。smart客戶端則通過(guò)內(nèi)部維護(hù)槽和節(jié)點(diǎn)的關(guān)系,本地就可以實(shí)現(xiàn)鍵和節(jié)點(diǎn)的查找。而MOVED重定向則只是協(xié)助客戶端維護(hù)槽和節(jié)點(diǎn)之間的關(guān)系。

  • jedis客戶端
Jedis客戶端命令執(zhí)行過(guò)程

故障轉(zhuǎn)移

與哨兵模式一樣,Redis集群模式也要解決分布式通用的部分失敗的問(wèn)題。

  1. 故障發(fā)現(xiàn)
    Redis集群內(nèi)通過(guò)ping/pong消息實(shí)現(xiàn)節(jié)點(diǎn)通信,消息不止攜帶節(jié)點(diǎn)槽信息,還攜帶有其他狀態(tài),比如主從節(jié)點(diǎn)狀態(tài)、節(jié)點(diǎn)故障燈。當(dāng)集群內(nèi)某個(gè)節(jié)點(diǎn)出現(xiàn)問(wèn)題時(shí),就會(huì)通過(guò)消息傳播
    與哨兵模式類似,存在主觀下線和客觀下線兩種狀態(tài)

    • 主觀下線:每個(gè)節(jié)點(diǎn)都會(huì)定期向其他節(jié)點(diǎn)發(fā)送ping消息,如果節(jié)點(diǎn)在cluster-node-timetout時(shí)間內(nèi)沒(méi)有收到pong消息,則該發(fā)送節(jié)點(diǎn),就會(huì)認(rèn)為對(duì)端存在故障。把接受節(jié)點(diǎn)標(biāo)記為主觀下線狀態(tài)(pfail)。

      typedef struct clusterState {
      clusterNode *myself; /* 自身節(jié)點(diǎn) /
      dict *nodes;/* 當(dāng)前集群內(nèi)所有節(jié)點(diǎn)的字典集合,key為節(jié)點(diǎn)ID,value為對(duì)應(yīng)節(jié)點(diǎn)ClusterNode結(jié)構(gòu) */
      ...
      } clusterState;字典nodes屬性中的clusterNode結(jié)構(gòu)保存了節(jié)點(diǎn)的狀態(tài),關(guān)鍵屬性如下:
      typedef struct clusterNode {
      int flags; /* 當(dāng)前節(jié)點(diǎn)狀態(tài),如:主從角色,是否下線等 */
      mstime_t ping_sent; /* 最后一次與該節(jié)點(diǎn)發(fā)送ping消息的時(shí)間 */
      mstime_t pong_received; /* 最后一次接收到該節(jié)點(diǎn)pong消息的時(shí)間 */
      ...
      } clusterNode;
      
    • 客觀下線
      當(dāng)某個(gè)節(jié)點(diǎn)判斷另一個(gè)節(jié)點(diǎn)主觀下線后,相應(yīng)的節(jié)點(diǎn)狀態(tài)會(huì)跟隨消息在集群內(nèi)傳播。當(dāng)半數(shù)以上持有槽的主節(jié)點(diǎn)都標(biāo)記某個(gè)節(jié)點(diǎn)是主觀下線時(shí),觸發(fā)客觀下線流程

故障恢復(fù)

當(dāng)故障節(jié)點(diǎn)變?yōu)榭陀^下線后,如果下線節(jié)點(diǎn)是持有槽的主節(jié)點(diǎn),則需要選擇一個(gè)從節(jié)點(diǎn)來(lái)替換它,從而保證高可用。下線主節(jié)點(diǎn)的所有從節(jié)點(diǎn)承擔(dān)故障恢復(fù)的義務(wù)。當(dāng)從節(jié)點(diǎn)通過(guò)內(nèi)部
定時(shí)任務(wù)發(fā)現(xiàn)主節(jié)點(diǎn)客觀下線時(shí),就會(huì)觸發(fā)故障恢復(fù)流程:

  1. 資格檢查
    判斷當(dāng)前節(jié)點(diǎn)是否有資格替換主節(jié)點(diǎn),判斷依據(jù)是,從節(jié)點(diǎn)與主節(jié)點(diǎn)斷線時(shí)間是否超過(guò)cluster-node-time*cluster-slave-validity-factor。如果超過(guò)則不具備3

  2. 準(zhǔn)備選舉時(shí)間
    當(dāng)從節(jié)點(diǎn)具備故障轉(zhuǎn)移資格后,更新觸發(fā)故障選舉的時(shí)間,只有到達(dá)該時(shí)間后才能執(zhí)行后續(xù)流程。這里之所以要采用延時(shí)觸發(fā)機(jī)制,主要是多個(gè)從節(jié)點(diǎn)有不同的優(yōu)先級(jí),也就是根據(jù)他們的優(yōu)先級(jí)來(lái)觸發(fā)故障選舉,優(yōu)先級(jí)更高的更容易成為主節(jié)點(diǎn),而優(yōu)先級(jí)的判斷就是通過(guò)offset(復(fù)制的偏移)。

  3. 發(fā)起選舉
    更新配置的epoch,每個(gè)主節(jié)點(diǎn)自身維護(hù)一個(gè)epoch表示當(dāng)前主節(jié)點(diǎn)的版本,所有主節(jié)點(diǎn)的epoch都不相等,從節(jié)點(diǎn)會(huì)復(fù)制主節(jié)點(diǎn)的epoch。整個(gè)集群有一個(gè)最大的epoch,也就是集群epoch。epoch會(huì)隨著消息的傳遞傳播下去,
    如果epoch相等,那么會(huì)根據(jù)nodeid大小來(lái)更新,nodeid更大的會(huì)使當(dāng)前集群epoch遞增,并且賦值給自己的epoch。每當(dāng)新節(jié)點(diǎn)加入、槽節(jié)點(diǎn)映射沖突、從節(jié)點(diǎn)投票選舉沖突時(shí)都會(huì)更新epoch。
    更新后會(huì)在集群內(nèi)廣播選舉消息(FAILOVER_AUTH_REQUEST),并記錄已發(fā)送過(guò)消息的狀態(tài)。保證該節(jié)點(diǎn)在一個(gè)epoch只能發(fā)起一次選舉。

  4. 選舉投票
    只有持有槽的主節(jié)點(diǎn)才會(huì)處理故障選舉消息,也就是上面說(shuō)的REQUET,在選舉過(guò)程中每個(gè)節(jié)點(diǎn)在同一個(gè)epoch中只有一張選票,當(dāng)收到第一個(gè)從節(jié)點(diǎn)消息是,就會(huì)回復(fù)ACK消息投出自己的選票,之后不會(huì)再響應(yīng)選舉請(qǐng)求。
    最終收到投票數(shù)大于一般的從節(jié)點(diǎn)會(huì)成為主節(jié)點(diǎn)。

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

    1. 取消復(fù)制,成為主節(jié)點(diǎn)
    2. 執(zhí)行clusterDelSlot操作,撤銷故障節(jié)點(diǎn)負(fù)責(zé)的槽,并執(zhí)行clusterAddSlot把這些槽委派給自己
    3. 向集群廣播自己的pong消息。通知其他節(jié)點(diǎn)更新信息。
  6. 故障轉(zhuǎn)移時(shí)間

    • 主觀下線識(shí)別時(shí)間=cluster-node-timeout
    • 傳播時(shí)間<=cluster-node-timeout/2
    • 從節(jié)點(diǎn)轉(zhuǎn)移時(shí)間<=1000毫秒
  7. 模擬故障轉(zhuǎn)移:

    # 主節(jié)點(diǎn)日志
    128026:M 28 Jan 2021 19:56:59.247 * Clear FAIL state for node 4aa937577181f82ce88b1c909a519ca03963ab21: is reachable again and nobody is serving its slots after some time.
    128026:M 28 Jan 2021 19:56:59.247 # Cluster state changed: ok
    128026:M 28 Jan 2021 19:58:38.299 * Marking node 4aa937577181f82ce88b1c909a519ca03963ab21 as failing (quorum reached).
    128026:M 28 Jan 2021 19:58:38.299 # Cluster state changed: fail
    128026:M 28 Jan 2021 19:58:38.809 # Failover auth granted to 4885dcbd061e28d969eb8d2ae893f256c9c79401 for epoch 9
    128026:M 28 Jan 2021 19:58:38.848 # Cluster state changed: ok
    128026:M 28 Jan 2021 19:58:45.127 * Clear FAIL state for node 4aa937577181f82ce88b1c909a519ca03963ab21: master without slots is reachable again.
    
    # 從節(jié)點(diǎn)日志
    128093:S 28 Jan 2021 19:58:38.805 # Starting a failover election for epoch 9.
    128093:S 28 Jan 2021 19:58:38.812 # Failover election won: I'm the new master.
    128093:S 28 Jan 2021 19:58:38.812 # configEpoch set to 9 after successful failover
    128093:M 28 Jan 2021 19:58:38.812 # Setting secondary replication ID to 2609636bd51ab7c2e436ee5653a8bf8a7445eefa, valid up to offset: 113. New replication ID is 3231e43113314a13c4878d95263a718da8fb305d
    128093:M 28 Jan 2021 19:58:38.812 * Discarding previously cached master state.
    128093:M 28 Jan 2021 19:58:38.812 # Cluster state changed: ok
    128093:M 28 Jan 2021 19:58:45.128 * Clear FAIL state for node 4aa937577181f82ce88b1c909a519ca03963ab21: master without slots is reachable again.
    

集群運(yùn)維

  1. 集群完整性
    默認(rèn)情況下當(dāng)集群16384個(gè)槽任何一個(gè)沒(méi)有指派到節(jié)點(diǎn)上,則執(zhí)行任何明林過(guò)度會(huì)報(bào)錯(cuò)。這是對(duì)集群的一種保護(hù),但當(dāng)主節(jié)點(diǎn)下線時(shí),從故障發(fā)現(xiàn)到完成轉(zhuǎn)移這個(gè)過(guò)程,整個(gè)集群時(shí)不可用狀態(tài)的,很多業(yè)務(wù)場(chǎng)景無(wú)法容忍這種情況
    因此可以將參數(shù)cluster-require-full-coverage配置為no,當(dāng)主節(jié)點(diǎn)故障時(shí),只影響它負(fù)責(zé)槽的相關(guān)命令執(zhí)行,不會(huì)影響其他節(jié)點(diǎn)。

  2. 帶寬消耗
    官方建議規(guī)模在1000以內(nèi),不然會(huì)造成大量網(wǎng)絡(luò)帶寬,2適當(dāng)提交cluster-node-timeout降低消息發(fā)送頻率、3盡量均勻的部署節(jié)點(diǎn)。

  3. Pub/Sub廣播問(wèn)題
    盡量使用sentinel模式使用廣播,集群模式會(huì)造成消息在所有節(jié)點(diǎn)廣播一遍,加重帶寬負(fù)擔(dān)。

  4. 集群傾斜

    • 節(jié)點(diǎn)和槽分配不均,可以使用rebalance命令,進(jìn)行平衡
    • 不同槽對(duì)應(yīng)鍵數(shù)量差異過(guò)大,一般使用CRC16哈希算法會(huì)相對(duì)均勻,但如果大量使用hash_tag時(shí),就會(huì)產(chǎn)生多個(gè)鍵映射到同一個(gè)槽的情況。
    • 集合對(duì)象包含大量元素,可以通過(guò)redis-cli bigkeys命令識(shí)別
    • 內(nèi)存配置不一致
  5. 手動(dòng)故障轉(zhuǎn)移
    Redis提供了手動(dòng)故障轉(zhuǎn)移功能,比如需要切換設(shè)備,重新調(diào)整部署方案的時(shí)候,可能會(huì)用到(直接kill會(huì)導(dǎo)致一定時(shí)間的不可用)。使用cluster failover命令

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • [TOC] 1. Redis集群介紹 Redis 集群是一個(gè)提供在多個(gè)Redis間節(jié)點(diǎn)間共享數(shù)據(jù)的程序集。 Red...
    為愛(ài)瘋狂_3850閱讀 438評(píng)論 0 0
  • 集群原理 一個(gè)系統(tǒng)建立集群主要需要解決兩個(gè):數(shù)據(jù)同步問(wèn)題和集群容錯(cuò)問(wèn)題。 Naive方案 一個(gè)簡(jiǎn)單粗暴的方案是部署...
    Java架構(gòu)007閱讀 276評(píng)論 0 3
  • 集群(Redis Cluster)作用: 1.數(shù)據(jù)分區(qū): 數(shù)據(jù)分區(qū)(或稱數(shù)據(jù)分片)是集群最核心的功能,集群將數(shù)據(jù)分...
    手扶拖拉機(jī)_6e4d閱讀 2,156評(píng)論 0 0
  • 1 集群的作用 集群,即Redis Cluster,是Redis 3.0開始引入的分布式存儲(chǔ)方案。集群由多個(gè)節(jié)點(diǎn)(...
    MiniSoulBigBang閱讀 326評(píng)論 0 0
  • Redis Cluster Specification 1 設(shè)計(jì)目標(biāo)和理由 1.1 Redis Cluster g...
    近路閱讀 4,392評(píng)論 0 12

友情鏈接更多精彩內(nèi)容