深刻學習Redis之Redis Cluster

Redis Cluster

在學習Redis Cluster以前,咱們先了解爲何須要集羣,當遇到單機內存、併發、流量等瓶頸時,單機已經沒法知足我讓節點7000和7001等節點進們的要求的時候,能夠採用Cluster架構方案達到負載均衡的目的。node

數據分區概論

分佈式數據庫首先要解決把整個數據集按照分區規則映射到多個節點的問題,即把數據集劃分到多個節點上,每一個節點負責總體數據的一個子集。redis

常見的分區規則有哈希分區和順序分區兩種。shell

首先看一下對比數據庫

分佈方式 特色 典型產品
哈希分區 數據分散度高、鍵值分佈無業務無關、沒法順序訪問、支持批量操做。 一致性哈希:Mecache、Redis Cluster ...
順序分區 數據分散度易傾斜、鍵值業務相關、能夠順序訪問、支持批量操做。 BigTable、HBase

順序分區

好比:1-100個數字,要保存到3個節點上,每一個節點平均存儲,1-33存儲在第1個節點,34-66存儲到2節點,剩餘存儲到3節點。緩存

順序存儲經常使用在關係型存儲上。架構

哈希分區

由於Redis Cluster採用的哈希分區,因此咱們看一下常見的哈希分區有哪幾種。併發

節點取餘分區

好比100個數據,對每一個數據進行hash運算以後,再於節點數進行取餘運算,根據餘數保存在不一樣節點上。負載均衡

缺點就是:當節點數量變化時,如擴容或收縮節點,數據節點映射關係須要從新計算,會致使數據的從新遷移。運維

一致性哈希分區

爲系統中每一個節點分配一個token,範圍通常在0~2的32次方,這些token構成一個哈希環。數據讀寫執行節點查找操做時,先根據key計算hash值,而後順時針找到第一個大於等於該哈希值的token節點,以下圖所示異步

這種方式相比節點取餘最大的好處在於加入和刪除節點隻影響哈希環中相鄰的節點,對其餘節點無影響

但一致性哈希也存在一些問題:

  • 加減節點會形成哈希環中部分數據沒法命中(例如一個key增減節點前映射到第n2個節點,所以它的數據是保存在第n2個節點上的;當咱們增長一個節點後被映射到n5節點上了,此時咱們去n5節點上去找這個key對應的值是找不到的,見下圖),須要手動處理或者忽略這部分數據,所以一致性哈希經常使用於緩存場景。

  • 當使用少許節點時,節點變化將大範圍影響哈希環中數據映射,所以這種方式不適合少許數據節點的分佈式方案。
  • 普通的一致性哈希分區在增減節點時須要增長一倍或減去一半節點才能保證數據和負載的均衡。

虛擬槽分區

Redis Cluster採用的就是虛擬槽分區。槽的範圍是0~16383,將16384個槽平均分配給節點,由節點進行管理。

每次將key進行hash運算,對16383進行取餘,而後去redis對應的槽進行查找。

槽是集羣內數據管理和遷移的基本單位。採用大範圍槽的主要目的是爲了方便數據拆分和集羣擴展。每一個節點會負責必定數量的槽。

好比咱們如今有5個集羣,每一個節點平均大約負責3276個槽。Redis Cluster 計算公式:slot=CRC16(key)&16383。每個節點負責維護一部分槽以及槽所映射的鍵值數據。

Redis虛擬槽分區的特色

  • 解耦數據和節點之間的關係,簡化了節點擴容和收縮難度。
  • 節點自身維護槽的映射關係,不須要客戶端或者代理服務維護槽分區元數據。
  • 支持節點、槽、鍵之間的映射查詢,用於數據路由、在線伸縮等場景。

準備節點

Redis集羣通常由多個節點組成,節點數量至少爲6個才能保證組成完整高可用的集羣。每一個節點須要開啓配置cluster-enabled yes,讓Redis運行在集羣模式下。

首先咱們在redis文件中建立三個文件夾:configdatalog。分別存放配置、數據和日誌相關文件。

配置相關redis.conf

#節點端口
port ${port}
 
# 守護進程模式啓動(可選)
daemonize yes
 
# 開啓集羣模式
cluster-enabled yes
 
# 節點超時時間,單位毫秒
cluster-node-timeout 15000
 
# 集羣內部配置文件
cluster-config-file /usr/local/redis/config/nodes-${port}.conf

# 節點宕機後是否整個集羣不可用
cluster-require-full-coverage no

dir /usr/local/redis/data/

dbfilename dump-${port}.rdb

logfile ${port}.log

# 其他的配置與redis.conf默認配置文件一致便可

6個節點所有配完成後就能夠開啓了。

[root@localhost config]# ls
redis-7000.conf  redis-7001.conf  redis-7002.conf  redis-7003.conf  redis-7004.conf  redis-7005.conf
[root@localhost redis]# redis-server config/redis-7000.conf
[root@localhost redis]# cd config
[root@localhost config]# cat nodes-7000.conf
f4deba14aac6494e95e3e4ad060c94b8c82df7ec :0 myself,master - 0 0 0 connected
vars currentEpoch 0 lastVoteEpoch 0
[root@localhost config]# cd ..
[root@localhost redis]# redis-server config/redis-7001.conf
[root@localhost redis]# redis-server config/redis-7002.conf
[root@localhost redis]# redis-server config/redis-7003.conf
[root@localhost redis]# redis-server config/redis-7004.conf
[root@localhost redis]# redis-server config/redis-7005.conf
[root@localhost redis]# cd config
[root@localhost config]# ll
總用量 288
-rw-r--r--. 1 root root   112 12月 17 04:00 nodes-7000.conf
-rw-r--r--. 1 root root   112 12月 17 04:00 nodes-7001.conf
-rw-r--r--. 1 root root   112 12月 17 04:00 nodes-7002.conf
-rw-r--r--. 1 root root   112 12月 17 04:00 nodes-7003.conf
-rw-r--r--. 1 root root   112 12月 17 04:00 nodes-7004.conf
-rw-r--r--. 1 root root   112 12月 17 04:00 nodes-7005.conf
-rw-r--r--. 1 root root 41650 12月 17 03:59 redis-7000.conf
-rw-r--r--. 1 root root 41649 12月 17 03:59 redis-7001.conf
-rw-r--r--. 1 root root 41651 12月 17 03:59 redis-7002.conf
-rw-r--r--. 1 root root 41651 12月 17 03:59 redis-7003.conf
-rw-r--r--. 1 root root 41651 12月 17 03:59 redis-7004.conf
-rw-r--r--. 1 root root 41651 12月 17 03:59 redis-7005.conf
[root@localhost config]# cat nodes-7005.conf
d1e8e8e42be8d3b2f3f44d197138e54d91170442 :0 myself,master - 0 0 0 connected
vars currentEpoch 0 lastVoteEpoch 0
[root@localhost config]#

檢查節點日誌是否正確:

sudo cat /usr/local/redis/conf/nodes-${port}.conf

文件內容記錄了集羣初始狀態,這裏最重要的是節點ID,它是一個40位16進制字符串,用於惟一標識集羣內一個節點,以後不少集羣操做都要藉助於節點ID來完成。須要注意是,節點ID不一樣於運行ID:節點ID在集羣初始化 時只建立一次,節點重啓時會加載集羣配置文件進行重用,而Redis的運行ID每次重啓都會變化。

咱們如今啓動6個節點,但每一個節點彼此並不知道對方的存在,下面經過節點握手讓6個節點彼此創建聯繫從而組成一個集羣

[root@localhost redis]# ps -ef |grep redis
root      1388     1  0 09:10 ?        00:00:00 redis-server *:7000 [cluster]
root      1392     1  0 09:10 ?        00:00:00 redis-server *:7001 [cluster]
root      1396     1  0 09:10 ?        00:00:00 redis-server *:7002 [cluster]
root      1400     1  0 09:10 ?        00:00:00 redis-server *:7003 [cluster]
root      1404     1  0 09:10 ?        00:00:00 redis-server *:7004 [cluster]
root      1408     1  0 09:10 ?        00:00:00 redis-server *:7005 [cluster]

節點握手

節點握手是指一批運行在集羣模式下的節點經過Gossip協議彼此通訊, 達到感知對方的過程

節點握手是集羣彼此通訊的第一步,由客戶端發起下面的命令:

cluster meet {ip} {port}
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7001
OK
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7002
OK
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7003
OK
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7004
OK
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7005
OK
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7006
OK

上面執行命令以後讓節點7000和7001等節點進行握手通訊。cluster meet命令是一個異步命令,執行以後馬上返回。內部發起與目標節點進行握手通訊。

  • 節點7000本地建立7001節點信息對象,併發送meet消息。
  • 節點7001接受到meet消息後,保存7000節點信息並回復pong消息。
  • 以後節點7000和7001彼此按期經過ping/pong消息進行正常的節點通訊。

這個時候咱們再執行cluster nodes能夠看到已經檢測到其它節點了。

[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster nodes
d1e8e8e42be8d3b2f3f44d197138e54d91170442 127.0.0.1:7005 master - 0 1609463858135 4 connected
9a8abb84bcc8301a8f11c664471159dc0bf23a62 127.0.0.1:7001 master - 0 1609463860149 1 connected
f4deba14aac6494e95e3e4ad060c94b8c82df7ec 127.0.0.1:7000 myself,master - 0 0 0 connected
d5f317fc4597dbaac8b26a5897d801a72e45512e 127.0.0.1:7003 master - 0 1609463857127 3 connected
7dbbf232c72405a66416d2a0c335bd072f740644 127.0.0.1:7004 master - 0 1609463859143 5 connected
d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 127.0.0.1:7002 master - 0 1609463861156 2 connected

節點創建握手以後集羣還不能正常工做,這時集羣處於下線狀態,全部的數據讀寫都被禁止。經過以下命令能夠看到:

[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000
127.0.0.1:7000> set jack hello
(error) CLUSTERDOWN The cluster is down

經過cluster info命令能夠獲取集羣當前狀態:

127.0.0.1:7000> 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:0
cluster_stats_messages_sent:670
cluster_stats_messages_received:521

能夠看到咱們如今的狀態是fail,被分配的槽 cluster_slots_assigned是0,因爲目前全部的槽沒有分配到節點,所以集羣沒法完成槽到節點的映射。只有當16384個槽所有分配給節點後,集羣才進入在線狀態

分配槽

Redis集羣把全部的數據映射到16384個槽中。每一個key會映射爲一個固定的槽,只有當節點分配了槽,才能響應和這些槽關聯的鍵命令。經過cluster addslots命令爲節點分配槽。由於咱們有6個節點,咱們是三主三從的模式,因此只用給三個主節點進行配置便可。

redis-cli -h 127.0.0.1 -p 7000 cluster addslots {0..5461}
redis-cli -h 127.0.0.1 -p 7001 cluster addslots {5462..10922}
redis-cli -h 127.0.0.1 -p 7002 cluster addslots {10923..16383}

配置成功後,咱們再進入節點看一下:

[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000
127.0.0.1:7000> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:5
cluster_my_epoch:0
cluster_stats_messages_sent:1384
cluster_stats_messages_received:1235

能夠看到,cluster_statecluster_slots_assigned都沒有問題。

設置主從

目前還有三個節點沒有使用,做爲一個完整的集羣,每一個負責處理槽的節點應該具備從節點,保證當它出現故障時能夠自動進行故障轉移。

集羣模式下,Reids節點角色分爲主節點和從節點。首次啓動的節點和被分配槽的節點都是主節點,從節點負責複製主節點槽信息和相關的數據。使用cluster replicate {node-id}命令讓一個節點成爲從節點。其中命令執行必須在對應的從節點上執行,node-id是要複製主節點的節點ID。

咱們首先找到三個已經配置槽的節點的node-id。

[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster nodes
d1e8e8e42be8d3b2f3f44d197138e54d91170442 127.0.0.1:7005 master - 0 1609464545892 4 connected
9a8abb84bcc8301a8f11c664471159dc0bf23a62 127.0.0.1:7001 master - 0 1609464547906 1 connected 5462-10922
f4deba14aac6494e95e3e4ad060c94b8c82df7ec 127.0.0.1:7000 myself,master - 0 0 0 connected 0-5461
d5f317fc4597dbaac8b26a5897d801a72e45512e 127.0.0.1:7003 master - 0 1609464546899 3 connected
7dbbf232c72405a66416d2a0c335bd072f740644 127.0.0.1:7004 master - 0 1609464549923 5 connected
d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 127.0.0.1:7002 master - 0 1609464548916 2 connected 10923-16383
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7003 cluster replicate f4deba14aac6494e95e3e4ad060c94b8c82df7ec
OK
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7004 cluster replicate 9a8abb84bcc8301a8f11c664471159dc0bf23a62
OK
[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7005 cluster replicate d438b4689776cb6cd6b6d0eaecb7576669c7b3fe
OK

完成後咱們查看是否已經ok。

[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster nodes
d1e8e8e42be8d3b2f3f44d197138e54d91170442 127.0.0.1:7005 slave d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 0 1609464847442 4 connected
9a8abb84bcc8301a8f11c664471159dc0bf23a62 127.0.0.1:7001 master - 0 1609464846435 1 connected 5462-10922
f4deba14aac6494e95e3e4ad060c94b8c82df7ec 127.0.0.1:7000 myself,master - 0 0 0 connected 0-5461
d5f317fc4597dbaac8b26a5897d801a72e45512e 127.0.0.1:7003 slave f4deba14aac6494e95e3e4ad060c94b8c82df7ec 0 1609464849456 3 connected
7dbbf232c72405a66416d2a0c335bd072f740644 127.0.0.1:7004 slave 9a8abb84bcc8301a8f11c664471159dc0bf23a62 0 1609464848449 5 connected
d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 127.0.0.1:7002 master - 0 1609464850468 2 connected 10923-16383

目前爲止,咱們依照Redis協議手動創建一個集羣。它由6個節點構成, 3個主節點負責處理槽和相關數據,3個從節點負責故障轉移。

Redis自動化安裝

咱們以前分別使用命令搭建了一個完整的集羣,可是命令過多,當集羣節點衆多時,必然會加大搭建集羣的複雜度和運維成本。所以redis還提供了redis-cli --cluster來搭建集羣

首先咱們仍是啓動六個單獨的節點。

使用下面命令進行安裝,--cluster-replicas 1 指定集羣中每一個主節點配備幾個從節點,這裏設置爲1。而且該命令會本身建立主節點和分配從節點,其中前3個是主節點,後3個是從節點,後3個從節點分別複製前3個主節點。

redis-cli --cluster create --cluster-replicas 1 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

最後的輸出報告說明:16384個槽所有被分配,集羣建立成功。這裏須要注意命令中節點的地址必須是不包含任何槽/數據的節點,不然會拒絕建立集羣。

若是不想要從節點則不填寫該參數便可--cluster-replicas 1

最後咱們可使用下面命令進行查看是否已經ok。

redis-cli --cluster check 127.0.0.1:7000

集羣伸縮原理

Redis集羣提供了靈活的節點擴容和收縮方案。在不影響集羣對外服務的狀況下,能夠爲集羣添加節點進行擴容也能夠下線部分節點進行縮容。原理可抽象爲槽和對應數據在不一樣節點之間靈活移動。

當咱們如今有三個節點,此時想增長6385節點,也就是每一個節點把一部分槽和數據遷移到新的節點6385,每一個節點負責的槽和數據相比以前變少了從而達到了集羣擴容的目的。

擴容集羣實操

準備節點

以前咱們有6個節點,7000~7005節點。

如今咱們增長兩個單獨的節點也就是7006和7007。而後7006節點當作主節點,7007當作從節點。新節點跟集羣內的節點配置保持一致,便於管理統一。

隨後咱們進行啓動

[root@localhost redis]# redis-server config/redis-7006.conf
[root@localhost redis]# redis-server config/redis-7007.conf

這個時候咱們的兩個新的節點只是單獨運行,並無加入集羣中。能夠看到下面並無7006和7007節點。

[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster nodes
d1e8e8e42be8d3b2f3f44d197138e54d91170442 127.0.0.1:7005 slave d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 0 1609467765084 4 connected
9a8abb84bcc8301a8f11c664471159dc0bf23a62 127.0.0.1:7001 master - 0 1609467769137 1 connected 5462-10922
f4deba14aac6494e95e3e4ad060c94b8c82df7ec 127.0.0.1:7000 myself,master - 0 0 0 connected 0-5461
d5f317fc4597dbaac8b26a5897d801a72e45512e 127.0.0.1:7003 slave f4deba14aac6494e95e3e4ad060c94b8c82df7ec 0 1609467767119 3 connected
7dbbf232c72405a66416d2a0c335bd072f740644 127.0.0.1:7004 slave 9a8abb84bcc8301a8f11c664471159dc0bf23a62 0 1609467768127 5 connected
d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 127.0.0.1:7002 master - 0 1609467766110 2 connected 10923-16383

結構圖以下:

加入集羣

redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7006
redis-cli -h 127.0.0.1 -p 7000 cluster meet 127.0.0.1 7007

集羣內新舊節點通過一段時間的ping/pong消息通訊以後,全部節點會發現新節點並將它們的狀態保存到本地。

隨後咱們再進行查看cluster nodes

[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster nodes
d1e8e8e42be8d3b2f3f44d197138e54d91170442 127.0.0.1:7005 slave d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 0 1609468208783 4 connected
9a8abb84bcc8301a8f11c664471159dc0bf23a62 127.0.0.1:7001 master - 0 1609468204768 1 connected 5462-10922
f4deba14aac6494e95e3e4ad060c94b8c82df7ec 127.0.0.1:7000 myself,master - 0 0 0 connected 0-5461
d5f317fc4597dbaac8b26a5897d801a72e45512e 127.0.0.1:7003 slave f4deba14aac6494e95e3e4ad060c94b8c82df7ec 0 1609468210798 3 connected
35f9f0abd365bb0fc424dbdaa849f1f1c71163bb 127.0.0.1:7006 master - 0 1609468209790 6 connected
55b028fbd0a0207b6acc6e2b1067bf79f3090534 127.0.0.1:7007 master - 0 1609468206777 7 connected
7dbbf232c72405a66416d2a0c335bd072f740644 127.0.0.1:7004 slave 9a8abb84bcc8301a8f11c664471159dc0bf23a62 0 1609468205773 5 connected
d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 127.0.0.1:7002 master - 0 1609468206274 2 connected 10923-16383

而後咱們把7007設置爲7006的從節點

redis-cli -h 127.0.0.1 -p 7007 cluster replicate 35f9f0abd365bb0fc424dbdaa849f1f1c71163bb

再次查看已經OK。

[root@localhost redis]# redis-cli -h 127.0.0.1 -p 7000 cluster nodes
d1e8e8e42be8d3b2f3f44d197138e54d91170442 127.0.0.1:7005 slave d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 0 1609470748800 4 connected
9a8abb84bcc8301a8f11c664471159dc0bf23a62 127.0.0.1:7001 master - 0 1609470750824 1 connected 5462-10922
f4deba14aac6494e95e3e4ad060c94b8c82df7ec 127.0.0.1:7000 myself,master - 0 0 0 connected 0-5461
d5f317fc4597dbaac8b26a5897d801a72e45512e 127.0.0.1:7003 slave f4deba14aac6494e95e3e4ad060c94b8c82df7ec 0 1609470745778 3 connected
35f9f0abd365bb0fc424dbdaa849f1f1c71163bb 127.0.0.1:7006 master - 0 1609470746785 6 connected
55b028fbd0a0207b6acc6e2b1067bf79f3090534 127.0.0.1:7007 slave 35f9f0abd365bb0fc424dbdaa849f1f1c71163bb 0 1609470751833 7 connected
7dbbf232c72405a66416d2a0c335bd072f740644 127.0.0.1:7004 slave 9a8abb84bcc8301a8f11c664471159dc0bf23a62 0 1609470749817 5 connected
d438b4689776cb6cd6b6d0eaecb7576669c7b3fe 127.0.0.1:7002 master - 0 1609470747795 2 connected 10923-16383

槽遷移計劃

上面咱們添加了兩個新節點:700六、7007。其中7006做爲主節點存儲數據,7007做爲從節點複製7006。下面咱們要把其餘節點的槽和數據遷移到7006這個節點中。

再遷移後原有節點負責的槽數量變爲4096個。

遷移數據

數據遷移過程是逐個槽進行的。流程以下:

  1. 對目標節點發送:cluster setslot {slot} importing {sourceNodeId}命令,讓目標節點準備導入槽數據。
  2. 對源節點發送:cluster setslot {slot} migrating {targetNodeId}命令,讓源節點準備遷出槽數據。
  3. 源節點循環執行:cluster getkeysinslot {slot} {count}命令,每次獲取count個屬於槽的鍵。
  4. 在源節點上執行:migrate {targetIP} {targetPort} key 0 {timeout}命令,把指定的key遷移。
  5. 重複執行步驟3和步驟4,直到槽下全部的鍵值數據遷移到目標節點。
  6. 向集羣內全部主節點發送:cluster setslot {slot} node {targetNodeId}命令,通知槽分配給目標節點。

僞代碼以下:

def move_slot(source,target,slot):
    # 目標節點準備導入槽
    target.cluster("setslot",slot,"importing",source.nodeId);
    # 源節點準備全出槽
    source.cluster("setslot",slot,"migrating",target.nodeId);
    while true :
        # 批量從源節點獲取鍵
        keys = source.cluster("getkeysinslot",slot,pipeline_size);
        if keys.length == 0:
            # 鍵列表爲空時,退出循環
            break;
        # 批量遷移鍵到目標節點
        source.call("migrate",target.host,target.port,"",0,timeout,"keys",keys);
    # 向集羣全部主節點通知槽被分配給目標節點
    for node in nodes:
        if node.flag == "slave":
            continue;
        node.cluster("setslot",slot,"node",target.nodeId);

redis-cli cluster進行遷移

redis-cli --cluster reshard host:port --from <arg> --to <arg> --slots <arg> --yes --timeout
<arg> --pipeline <arg>
  • host:port:必傳參數,集羣內任意節點地址,用來獲取整個集羣信息。
  • --from:制定源節點的id,若是有多個源節點,使用逗號分隔,若是是all源節點變爲集羣內全部主節點,在遷移過程當中提示用戶輸入。
  • --to:須要遷移的目標節點的id,目標節點只能填寫一個,在遷移過程 中提示用戶輸入。
  • --slots:須要遷移槽的總數量,在遷移過程當中提示用戶輸入。
  • --yes:當打印出reshard執行計劃時,是否須要用戶輸入yes確認後再執行reshard。
  • --timeout:控制每次migrate操做的超時時間,默認爲60000毫秒。
  • ·--pipeline:控制每次批量遷移鍵的數量,默認爲10。

開始遷移:

redis-cli --cluster reshard 127.0.0.1:7000

輸入須要遷移的槽數量,此處咱們輸入4096。

目標節點ID,只能指定一個,由於咱們須要遷移到7006中,所以下面輸入7006的ID。

以後輸入源節點的ID,redis會從這些源節點中平均取出對應數量的槽,而後遷移到6385中,下面咱們分別輸入7000、700一、7002的節點ID。最後要輸入done表示結束。

最後輸入yes便可。

咱們能夠檢查一下節點之間的平衡性

redis-cli --cluster rebalance 127.0.0.1:6380

全部主節點負責的槽數量差別在2%之內,就算集羣節點數據相對均勻,無需調整

收縮集羣

  • 首先須要肯定下線節點是否有負責的槽,若是是,須要把槽遷移到 其餘節點,保證節點下線後整個集羣槽節點映射的完整性。
  • 當下線節點再也不負責槽或者自己是從節點時,就能夠通知集羣內其 他節點忘記下線節點,當全部的節點忘記該節點後能夠正常關閉。

收縮正好和擴容遷移方向相反,7006變爲源節點,其餘主節點變爲目標節點,源節點須要把自身負責的4096個槽均勻地遷移到其餘主節點上。

具體步驟和上述擴容相似,這裏就不演示。

請求重定向

在集羣模式下,Redis接收任何鍵相關命令時首先計算鍵對應的槽,再根據槽找出所對應的節點,若是節點是自身,則處理鍵命令;不然回覆MOVED重定向錯誤,通知客戶端請求正確的節點。

命中槽

由於咱們執行cluster keyslot hello以後,發現槽的位置在866,在咱們之中,因此直接返回。

127.0.0.1:7000> set hello world
OK
127.0.0.1:7000> cluster keyslot hello
(integer) 866
127.0.0.1:7000> get hello
"world"

未命中槽

因爲鍵對應槽是6918,不屬於7000節點,則回覆MOVED {slot} {ip} {port}格式重定向信息:

127.0.0.1:7000> set test hello
(error) MOVED 6918 127.0.0.1:7001

咱們能夠切換到7001發送命令便可成功。

127.0.0.1:7001> set test hello
OK

用redis-cli命令時,能夠加入-c參數支持自動重定向,簡化手動發起重定向操做。

[root@localhost config]# redis-cli -h 127.0.0.1 -p 7000 -c
127.0.0.1:7000> set test hello
-> Redirected to slot [6918] located at 127.0.0.1:7001
OK

ASK重定向

Redis集羣支持在線遷移槽(slot)和數據來完成水平伸縮,當slot對應的數據從源節點到目標節點遷移過程當中,客戶端須要作到智能識別,保證鍵命令可正常執行。例如當一個slot數據從源節點遷移到目標節點時,期間可能出現一部分數據在源節點,而另外一部分在目標節點。

當出現上述狀況時,客戶端鍵命令執行流程將發生變化,以下所示:

  1. 客戶端根據本地slots緩存發送命令到源節點,若是存在鍵對象則直 接執行並返回結果給客戶端。
  2. 若是鍵對象不存在,則可能存在於目標節點,這時源節點會回覆 ASK重定向異常。格式以下:(error) ASK {slot} {targetIP}:{targetPort}
  3. 客戶端從ASK重定向異常提取出目標節點信息,發送asking命令到目標節點打開客戶端鏈接標識,再執行鍵命令。若是存在則執行,不存在則返回不存在信息。

ASK和MOVED區別

  • ASK重定向說明集羣正在進行slot數據遷移,客戶端沒法知道何時遷移完成,所以只能是臨時性的重定向,客戶端不會更新slots緩存。
  • 可是MOVED重定向說明鍵對應的槽已經明確指定到新的節點,所以須要更新slots緩存。

故障發現

  • 當集羣內某個節點出現問題時,須要經過一種健壯的方式保證識別出節點是否發生了故障。Redis集羣內節點經過ping/pong消息實現節點通訊,消息不但能夠傳播節點槽信息,還能夠傳播其餘狀態如:主從狀態、節點故障等。
  • 所以故障發現也是經過消息傳播機制實現的,主要環節包括:

    • 主觀下線 (pfail):指某個節點認爲另外一個節點不可用,即下線狀態,這個狀態並非最終的故障斷定,只能表明一個節點的意見,可能存在誤判狀況。
    • 客觀下線(fail):指標記一個節點真正的下線,集羣內多個節點都認爲該節點不可用,從而達成共識的結果。若是是持有槽的主節點故障,須要爲該節點進行故障轉移。

主觀下線

集羣中每一個節點都會按期向其餘節點發送ping消息,接收節點回復pong消息做爲響應。若是在cluster-node-timeout時間內通訊一直失敗,則發送節點會認爲接收節點存在故障,把接收節點標記爲主觀下線(pfail)狀態

主觀下線流程:

  1. 節點a發送ping消息給節點b,若是通訊正常將接收到pong消息,節點a更新最近一次與節點b的通訊時間。
  2. 若是節點a與節點b通訊出現問題則斷開鏈接,下次會進行重連。若是一直通訊失敗,則節點a記錄的與節點b最後通訊時間將沒法更新。
  3. 節點a內的定時任務檢測到與節點b最後通訊時間超過cluster-nodetimeout時,更新本地對節點b的狀態爲主觀下線(pfail)。

客觀下線

當半數以上持有槽的主節點都標記某節點主觀下線。

客觀下線流程:

  1. 當消息體內含有其餘節點的pfail狀態會判斷髮送節點的狀態,若是發送節點是主節點則對報告的pfail狀態處理,從節點則忽略
  2. 找到pfail對應的節點結構,更新clusterNode內部下線報告鏈表。
  3. 根據更新後的下線報告鏈表告嘗試進行客觀下線。

嘗試客觀下線

  1. 首先統計有效的下線報告數量,若是小於集羣內持有槽的主節點總數的一半則退出
  2. 當下線報告大於槽主節點數量一半時,標記對應故障節點爲客觀下線狀態。
  3. 向集羣廣播一條fail消息,通知全部的節點將故障節點標記爲客觀下線,fail消息的消息體只包含故障節點的ID。

故障恢復

故障節點變爲客觀下線後,若是下線節點是持有槽的主節點則須要在它的從節點中選出一個替換它,從而保證集羣的高可用。下線主節點的全部從節點承擔故障恢復的義務,當從節點經過內部定時任務發現自身複製的主節點進入客觀下線時,將會觸發故障恢復流程。

檢查資格

  • 每一個從節點都要檢查最後與主節點斷線時間,判斷是否有資格替換故障的主節點
  • 若是從節點與主節點斷線時間超過cluster-node-timeout * cluster-slave-validity-factor,則當前從節點不具有故障轉移資格。參數cluster-slavevalidity-factor用於從節點的有效因子,默認爲10。

準備選舉時間

當從節點符合故障轉移資格後,更新觸發故障選舉的時間,只有到達該時間後才能執行後續流程

主節點b進入客觀下線後,它的三個從節點根據自身複製偏移量設置延遲選舉時間,如複製偏移量最大的節點slave b-1延遲1秒執行,保證複製延遲低的從節點優先發起選舉。

選舉投票

  • 只有持有槽的主節點纔會處理故障選舉消息。
  • 投票過程實際上是一個領導者選舉的過程,如集羣內有N個持有槽的主節點表明有N張選票。因爲在每一個配置紀元內持有槽的主節點只能投票給一個從節點,所以只能有一個從節點得到N/2+1的選票,保證可以找出惟一的從節點。
  • Redis集羣沒有直接使用從節點進行領導者選舉,主要由於從節點數必須大於等於3個才能保證湊夠N/2+1個節點,將致使從節點資源浪費。使用集羣內全部持有槽的主節點進行領導者選舉,即便只有一個從節點也能夠完成選舉過程。
  • 當從節點收集到N/2+1個持有槽的主節點投票時,從節點能夠執行替換主節點操做,例如集羣內有5個持有槽的主節點,主節點b故障後還有4個, 當其中一個從節點收集到3張投票時表明得到了足夠的選票能夠進行替換主節點操做。

替換主節點

當從節點收集到足夠的選票以後,觸發替換主節點操做:

  • 當前從節點取消複製變爲主節點
  • 執行clusterDelSlot操做撤銷故障主節點負責的槽,並執行clusterAddSlot把這些槽委派給本身。
  • 向集羣廣播本身的pong消息,代表已經替換了故障從節點。
相關文章
相關標籤/搜索