Redis Cluster集群搭建及節點的添加、刪除


 

一、什么是 Redis Cluster


Redis 是在內存中保存數據的,而我們的電腦一般內存都不大,這也意味着 Redis 不適合存儲大數據,適合存儲大數據的是 Hadoop 生態系統的 Hbase 或者是 MogoDB 。Redis 更適合處理高並發,一台設備的存儲能力是有限的但是多台設備協同合作,就可以內存增大很多倍,這時就需要集群。

Redis 集群搭建的方式有很多種,但從 redis 3.0 版本之后,支持 redis-cluster 集群,它是 Redis 官方提供的解決方案,Redis Cluster 采用的是 無中心架構 ,每個節點保存數據和整個集群狀態,每個節點都和其他節點有所連接。Redis Cluster的基本原理可以從數據分片數據遷移集群通訊故障檢測以及故障轉移等方面進行了解,其架構如下:

客戶端與 redis 節點直連,不需要中間件 proxy 層,客戶端不需要連接集群所有節點,連接集群匯中任何一個節點即可。所有的 redis 節點彼此互聯(PING-PONG 機制),內部使用二進制協議優化傳輸速度和帶寬。

二、分布式存儲機制-槽


【1】Redis Cluster 在設計中沒有使用一致性哈希(Consistency Hashing),而是使用數據分片(Sharding)引入哈希槽
【2】Redis Cluster 把所有的節點映射到 [0-16383] slot 槽上,cluster 負責維護 node<->slot<->value 三者之間的關系。
【3】Redis 集群中內置了 16384 個哈希槽,當需要在 Redis 集群中放置一個 key-value 時,redis 先將 key 使用 CRC16 算法算出一個結果,然后把結果對 16384 求余數,這樣每個 key 都會對應一個編號 0-16383 之間的哈希槽,redis 會根據節點數量大致均等的將哈希槽映射到不同的節點上。集群之間通過一種特殊的二進制協議交互集群信息。

例如,當有三個節點時,槽分布的值如下:
節點1:     0-5460
節點2:     5461-10921
節點3:     10922-16383

【4】Redis Cluster 允許用戶強制把某個 key 掛在特定槽位上。通過在 key 字符串里面嵌入 tag 標記,這就可以強制 key 所掛的槽位等於 tag 所在的槽位。
【5】客戶端為了可以直接定位某個具體的 key 所在的節點,需要緩存槽位相關信息,從而實現快速定位。同時因為客戶端與服務端的槽位可能不一致,還需要糾正機制來實現槽位信息的校驗調整。
【6】Redis Cluster 的每個節點會將集群的配置信息持久化到配置文件中,所以必須確保配置文件可寫,而且盡量不要依靠人工修改配置文件。
【7】ClusterNode 數據結構中的 slots和 numslots屬性記錄了節點負責處理哪些槽。其中,slot屬性是一個二進制位數組(bitarray),其長度為16384/8=2048 Byte,共包含16384個二進制位。集群中的 Master節點用bit(0和1)來標識是否擁有某個槽。比如,對於編號為1的槽,Master只要判斷序列第二位(索引從0開始)的值是不是1即可,時間復雜度為O(1)。


集群中所有槽的分配信息都保存在 ClusterState數據結構的 slots數組中,程序要檢查槽是否已經被分配或者找出處理槽i的節點,只需要訪問 clusterState.slots[i] 的值即可,復雜度也為O(1)。ClusterState數據結構如圖所示。

查找關系圖】:

三、容錯機制投票


【1】選舉過程是集群中所有 master 節點參與,如果半數以上 master 節點與故障節點通信超過設置的(cluster-node-timeout),認為該節點故障,自動觸發故障轉移操作。故障節點的從節點自動升級為主節點。
【2】如果集群任意 master 掛掉,且當前 master 沒有 slave。集群進入 fail 狀態,也可以理解成集群的 slot 映射[0-16383]找不到對應的槽時進入 fail 狀態。
 

四、搭建Redis-Cluster


准備工作:准備六台 Redis 服務器,三台 Master 主機,三台 Slave 備機(我們因條件問題,下面六台服務器均出自一台計算機,只是端口不一致(從7001-7006))
【1】下載 Redis 的源碼文件,進行解壓(tar -zxvf redis-3.0.0.tar.gz),進入解碼目錄,對 C語言開發的 Redis 進行編譯[make],編譯完成后創建安裝的目錄(/usr/local/redis-cluster/redis-1 等),執行命令進行安裝(make install PREFIX=/usr/local/redis-cluster/redis-1 等)
【2】復制配置文件,將 /redis-3.0.0/redis.conf 復制到 redis下的 bin目錄下(cp redis.conf /usr/local/redis-cluster/redis-1/bin)
【3】修改六台服務器中的bin/redis.conf 配置文件,將配置集群字段前的注釋去掉: # cluster-enabled yes 並修改端口(45行:port 7001)
【4】安裝 Ruby 環境:

yum -y install ruby
yum -y install rubygems

【5】網上下載 redis-3.0.0.gem,執行 gem install redis-3.0.0.gem 安裝。
【6】使用 ruby 腳本搭建集群:進入 Redis 源碼目錄中的 src 目錄,執行如下命令:需要更換 IP 地址

./redis-trib.rb create --replicas 1 192.168.88.131:7001 192.168.88.131:7002 192.168.88.131:7003 192.168.88.131:7004 192.168.88.131:7005 192.168.88.131:7006

 出現如下情況時表示集群成功:如圖所示,7001至7003為主機,7004至7006為備機
 
然后輸入 yes 輸入如下信息時,則表示配置成功:成功后你會發現其他節點都會有變化

五、客戶端連接 RedisCluster


進入可以連接 Redis 的客戶端文件中,例如:我在 Windows 環境下使用的是 redis-cli.exe 可執行文件。通過運行如下命令:進入 Redis 集群(-h:連接的主機地址、-p:連接的端口、-c:表示集群環境,不寫表示連接的單機。Redis只需連接一個節點即可進入集群環境,可以通過 quit 命令退出 Redis 客戶端連接。通過客戶端關閉Redis服務:./redis-cli -h 地址 -p 端口 shutdown)。

【跳轉】:當客戶端向一個錯誤的節點發出了指令后,該節點會發現指令的 key 所在的槽位並不歸自己管理,這時它會向客戶端發送一個特殊的跳轉指令攜帶目標操作的節點地址,告訴客戶端去連接這個節點以獲取數據。

1 GET x
2 -MOVED 3999 127.0.0.1:8283

  ■  -MOVED:前面有個減號,表示該指令是一個錯誤消息。客戶端在收到 MOVED 指令后,要立即糾正本地的槽位映射表。后續所有key 將使用新的槽位映射;
  ■  3999:key 對應的槽位編號;
  ■ 127.0.0.1:8283:目標節點地址;

六、通過 SpringDataRedis 連接 Redis 集群:主要是兩個配置文件


 redis-cluster-config.properties(主要用於配置可變的服務器地址和端口)

 1 #cluster configuration
 2 redis.host1=192.168.88.131
 3 redis.port1=7001
 4 
 5 redis.host2=192.168.88.131
 6 redis.port2=7002
 7 
 8 redis.host3=192.168.88.131
 9 redis.port3=7003
10 
11 redis.host4=192.168.88.131
12 redis.port4=7004
13 
14 redis.host5=192.168.88.131
15 redis.port5=7005
16 
17 redis.host6=192.168.88.131
18 redis.port6=7006
19 
20 redis.maxRedirects=3
21 redis.maxIdle=100
22 redis.maxTotal=600

 applicationContext-redis-cluster.xml(從 properties 獲取可變的參數作為屬性傳入集群類(redis-clusterConfiguration)中) 

 1 <?xml version="1.0" encoding="UTF-8"?> 
 2 <beans xmlns="http://www.springframework.org/schema/beans" 
 3   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" 
 4   xmlns:context="http://www.springframework.org/schema/context" 
 5   xsi:schemaLocation="http://www.springframework.org/schema/beans   
 6             http://www.springframework.org/schema/beans/spring-beans.xsd   
 7             http://www.springframework.org/schema/context   
 8             http://www.springframework.org/schema/context/spring-context.xsd">  
 9     <!-- 加載配置屬性文件 -->  
10 <context:property-placeholder ignore-unresolvable="true" location="classpath:properties/redis-cluster-config.properties" />  
11 <bean id="redis-clusterConfiguration" class="org.springframework.data.redis.connection.redis-clusterConfiguration">  
12     <property name="maxRedirects" value="${redis.maxRedirects}"></property>  
13     <property name="clusterNodes">  
14     <set>  
15         <bean class="org.springframework.data.redis.connection.redis-clusterNode">  
16             <constructor-arg name="host" value="${redis.host1}"></constructor-arg>  
17         <constructor-arg name="port" value="${redis.port1}"></constructor-arg>  
18         </bean>  
19         <bean class="org.springframework.data.redis.connection.redis-clusterNode">  
20             <constructor-arg name="host" value="${redis.host2}"></constructor-arg>  
21         <constructor-arg name="port" value="${redis.port2}"></constructor-arg>  
22         </bean>  
23         <bean class="org.springframework.data.redis.connection.redis-clusterNode">  
24         <constructor-arg name="host" value="${redis.host3}"></constructor-arg>  
25         <constructor-arg name="port" value="${redis.port3}"></constructor-arg>  
26         </bean>  
27         <bean class="org.springframework.data.redis.connection.redis-clusterNode">  
28             <constructor-arg name="host" value="${redis.host4}"></constructor-arg>  
29             <constructor-arg name="port" value="${redis.port4}"></constructor-arg>  
30          </bean>  
31          <bean class="org.springframework.data.redis.connection.redis-clusterNode">  
32             <constructor-arg name="host" value="${redis.host5}"></constructor-arg>  
33             <constructor-arg name="port" value="${redis.port5}"></constructor-arg>  
34          </bean>  
35          <bean class="org.springframework.data.redis.connection.redis-clusterNode">  
36             <constructor-arg name="host" value="${redis.host6}"></constructor-arg>  
37         <constructor-arg name="port" value="${redis.port6}"></constructor-arg>  
38          </bean>  
39      </set>  
40      </property>  
41 </bean>  
42 <bean id="jedisPoolConfig"   class="redis.clients.jedis.JedisPoolConfig">  
43         <property name="maxIdle" value="${redis.maxIdle}" />   
44     <property name="maxTotal" value="${redis.maxTotal}" />   
45 </bean>  
46 <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"  >  
47         <constructor-arg ref="redis-clusterConfiguration" />  
48         <constructor-arg ref="jedisPoolConfig" />  
49 </bean>    
50 <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">  
51         <property name="connectionFactory" ref="jedisConnectionFactory" />  
52 </bean>  
53 </beans>

七、查詢集群狀態信息


  通過cluster info可查詢集群狀態信息

 1 192.168.88.131:7001> cluster info
 2 cluster_state:ok                        #集群狀態
 3 cluster_slots_assigned:16384            #被分配的槽位數
 4 cluster_slots_ok:16384                  #正確分配的槽位數
 5 cluster_slots_pfail:0
 6 cluster_slots_fail:0
 7 cluster_known_nodes:6                   #集群的節點個數
 8 cluster_size:3
 9 cluster_current_epoch:9
10 cluster_my_epoch:1
11 cluster_stats_messages_sent:9769
12 cluster_stats_messages_received:9740

八、添加主節點


【1】集群創建成功后,向集群創建添加 master 節點,准備一個 Redis 節點(7007 端口)並修改配置文件,隨后將其啟動成功,再新打開一個窗口,進入存放 redis-trib.rb 文件的目錄下:執行如下命令:

 1 [root@learnVM redis-cluster]# ./redis-trib.rb add-node 192.168.88.131:7007 192.168.88.131:7001
 2 >>> Adding node 192.168.88.131:7007 to cluster 192.168.88.131:7001
 3 Connecting to node 192.168.88.131:7001: OK
 4 Connecting to node 192.168.88.131:7002: OK
 5 Connecting to node 192.168.88.131:7005: OK
 6 Connecting to node 192.168.88.131:7006: OK
 7 Connecting to node 192.168.88.131:7004: OK
 8 Connecting to node 192.168.88.131:7003: OK
 9 >>> Performing Cluster Check (using node 192.168.88.131:7001)
10 M: bd2b45a9395aafd3104291b4fb57a53cc12238db 192.168.88.131:7001
11    slots:0-5460 (5461 slots) master
12    1 additional replica(s)
13 S: 2483a4a01a6cdbce33dfd58d08463cfe3651dbb4 192.168.88.131:7002
14    slots: (0 slots) slave
15    replicates 3628651a416fa8e8b8b82eff2608173ca75937a5
16 M: 3628651a416fa8e8b8b82eff2608173ca75937a5 192.168.88.131:7005
17    slots:5461-10922 (5462 slots) master
18    1 additional replica(s)
19 S: 5f3f7324507788d072f4c9e13b428cfac5bcfaef 192.168.88.131:7006
20    slots: (0 slots) slave
21    replicates 11e83e152aaa229325db94c4bbb7c846872a94cc
22 S: a0d5b3f18bbb0cb37c0bf92aaca97ac8ba980d10 192.168.88.131:7004
23    slots: (0 slots) slave
24    replicates bd2b45a9395aafd3104291b4fb57a53cc12238db
25 M: 11e83e152aaa229325db94c4bbb7c846872a94cc 192.168.88.131:7003
26    slots:10923-16383 (5461 slots) master
27    1 additional replica(s)
28 [OK] All nodes agree about slots configuration.
29 >>> Check for open slots...
30 >>> Check slots coverage...
31 [OK] All 16384 slots covered.
32 Connecting to node 192.168.88.131:7007: OK
33 >>> Send CLUSTER MEET to node 192.168.88.131:7007 to make it join the cluster.
34 [OK] New node added correctly.

【2】在客戶端執行:cluster nodes 查看集群節點:會發現新添加的集群節點 7007(作為master卻沒有槽數<最后面的x-x>)

1 192.168.88.131:7001> cluster nodes
2 bd2b45a9395aafd3104291b4fb57a53cc12238db 192.168.88.131:7001 myself,master - 0 0 1 connected 0-5460
3 f4451c0e7d525303897e202cb504f7880b64e4fd 192.168.88.131:7007 master - 0 1562167495488 0 connected
4 2483a4a01a6cdbce33dfd58d08463cfe3651dbb4 192.168.88.131:7002 slave 3628651a416fa8e8b8b82eff2608173ca75937a5 0 1562167497511 10 connected
5 3628651a416fa8e8b8b82eff2608173ca75937a5 192.168.88.131:7005 master - 0 1562167497007 10 connected 5461-10922
6 5f3f7324507788d072f4c9e13b428cfac5bcfaef 192.168.88.131:7006 slave 11e83e152aaa229325db94c4bbb7c846872a94cc 0 1562167496501 8 connected
7 a0d5b3f18bbb0cb37c0bf92aaca97ac8ba980d10 192.168.88.131:7004 slave bd2b45a9395aafd3104291b4fb57a53cc12238db 0 1562167493969 4 connected
8 11e83e152aaa229325db94c4bbb7c846872a94cc 192.168.88.131:7003 master - 0 1562167494477 8 connected 10923-16383

 【3】hash 槽從新分配:添加完主節點需要對主節點進行hash槽分配這樣該主節才可以存儲數據。redis 集群有16384個槽,集群中的每個 master 結點分配一些槽,通過查看集群結點可以看到槽占用情況。開始分配:執行如下命令

 1 [root@learnVM redis-cluster]# ./redis-trib.rb reshard 192.168.88.131:7001
 2 Connecting to node 192.168.88.131:7001: OK
 3 Connecting to node 192.168.88.131:7007: OK
 4 Connecting to node 192.168.88.131:7002: OK
 5 Connecting to node 192.168.88.131:7005: OK
 6 Connecting to node 192.168.88.131:7006: OK
 7 Connecting to node 192.168.88.131:7004: OK
 8 Connecting to node 192.168.88.131:7003: OK
 9 >>> Performing Cluster Check (using node 192.168.88.131:7001)
10 M: bd2b45a9395aafd3104291b4fb57a53cc12238db 192.168.88.131:7001
11    slots:0-5460 (5461 slots) master
12    1 additional replica(s)
13 M: f4451c0e7d525303897e202cb504f7880b64e4fd 192.168.88.131:7007
14    slots: (0 slots) master
15    0 additional replica(s)
16 S: 2483a4a01a6cdbce33dfd58d08463cfe3651dbb4 192.168.88.131:7002
17    slots: (0 slots) slave
18    replicates 3628651a416fa8e8b8b82eff2608173ca75937a5
19 M: 3628651a416fa8e8b8b82eff2608173ca75937a5 192.168.88.131:7005
20    slots:5461-10922 (5462 slots) master
21    1 additional replica(s)
22 S: 5f3f7324507788d072f4c9e13b428cfac5bcfaef 192.168.88.131:7006
23    slots: (0 slots) slave
24    replicates 11e83e152aaa229325db94c4bbb7c846872a94cc
25 S: a0d5b3f18bbb0cb37c0bf92aaca97ac8ba980d10 192.168.88.131:7004
26    slots: (0 slots) slave
27    replicates bd2b45a9395aafd3104291b4fb57a53cc12238db
28 M: 11e83e152aaa229325db94c4bbb7c846872a94cc 192.168.88.131:7003
29    slots:10923-16383 (5461 slots) master
30    1 additional replica(s)
31 [OK] All nodes agree about slots configuration.
32 >>> Check for open slots...
33 >>> Check slots coverage...
34 [OK] All 16384 slots covered.
35 How many slots do you want to move (from 1 to 16384)? 

 ☛  出現上述的:How many slots do you want to move(是詢問你需要分配的槽大小:我們就輸入1000<輸入1000表示要分配1000個槽>)

What is the receiving node ID? f4451c0e7d525303897e202cb504f7880b64e4fd

 ☛  出現上述情況:詢問需要分配槽的節點id:我們就輸入7007的節點ID(就是剛才執行 cluster nodes 返回的第一行數據)

1 Please enter all the source node IDs.
2   Type 'all' to use all the nodes as source nodes for the hash slots.
3   Type 'done' once you entered all the source nodes IDs.
4 Source node #1:

 ☛  輸入源節點 id:槽將從源節點中拿,分配后的槽在源節點就不存在了。輸入 all 將從所有源節點開始拿,done 取消分配。

1 ......
2     Moving slot 11254 from 11e83e152aaa229325db94c4bbb7c846872a94cc
3     Moving slot 11255 from 11e83e152aaa229325db94c4bbb7c846872a94cc
4 Do you want to proceed with the proposed reshard plan (yes/no)? yes

 ☛  輸入 yes 開始移動槽到目標結點 id

1 Moving slot 11253 from 192.168.88.131:7003 to 192.168.88.131:7007: 
2 Moving slot 11254 from 192.168.88.131:7003 to 192.168.88.131:7007: 
3 Moving slot 11255 from 192.168.88.131:7003 to 192.168.88.131:7007: 
4 [root@learnVM redis-cluster]# 

【4】重新查看節點信息: 7007的槽(0-332 5461-5794 10923-11255)是來自其他三個master節點的部分槽。

1 192.168.88.131:7001> cluster nodes
2 bd2b45a9395aafd3104291b4fb57a53cc12238db 192.168.88.131:7001 myself,master - 0 0 1 connected 333-5460
3 f4451c0e7d525303897e202cb504f7880b64e4fd 192.168.88.131:7007 master - 0 1562169453293 11 connected 0-332 5461-5794 10923-11255
4 2483a4a01a6cdbce33dfd58d08463cfe3651dbb4 192.168.88.131:7002 slave 3628651a416fa8e8b8b82eff2608173ca75937a5 0 1562169456321 10 connected
5 3628651a416fa8e8b8b82eff2608173ca75937a5 192.168.88.131:7005 master - 0 1562169450269 10 connected 5795-10922
6 5f3f7324507788d072f4c9e13b428cfac5bcfaef 192.168.88.131:7006 slave 11e83e152aaa229325db94c4bbb7c846872a94cc 0 1562169449259 8 connected
7 a0d5b3f18bbb0cb37c0bf92aaca97ac8ba980d10 192.168.88.131:7004 slave bd2b45a9395aafd3104291b4fb57a53cc12238db 0 1562169454300 4 connected
8 11e83e152aaa229325db94c4bbb7c846872a94cc 192.168.88.131:7003 master - 0 1562169455309 8 connected 11256-16383

九、添加從節點


集群創建成功后,可以向集群中插入一個 slave 從節點(准備一個 7008 端口的 Redis並將其啟動成功,我們將其配置為 7007 的從節點),打開新的窗口,進入存放 redis-trib.rb 文件的目錄下,執行如下命令(格式為:./redis-trib.rb add-node --slave --master-id 主節點id 添加節點的ip和端口 集群中已存在節點ip和端口):其中主節點ID,通過 cluster nodes 查詢獲取。

 1 [root@learnVM redis-cluster]# ./redis-trib.rb add-node --slave --master-id f4451c0e7d525303897e202cb504f7880b64e4fd 192.168.88.131:7008 192.168.88.131:7001
 2 >>> Adding node 192.168.88.131:7008 to cluster 192.168.88.131:7001
 3 Connecting to node 192.168.88.131:7001: OK
 4 Connecting to node 192.168.88.131:7007: OK
 5 Connecting to node 192.168.88.131:7002: OK
 6 Connecting to node 192.168.88.131:7005: OK
 7 Connecting to node 192.168.88.131:7006: OK
 8 Connecting to node 192.168.88.131:7004: OK
 9 Connecting to node 192.168.88.131:7003: OK
10 >>> Performing Cluster Check (using node 192.168.88.131:7001)
11 M: bd2b45a9395aafd3104291b4fb57a53cc12238db 192.168.88.131:7001
12    slots:333-5460 (5128 slots) master
13    1 additional replica(s)
14 M: f4451c0e7d525303897e202cb504f7880b64e4fd 192.168.88.131:7007
15    slots:0-332,5461-5794,10923-11255 (1000 slots) master
16    0 additional replica(s)
17 M: 2483a4a01a6cdbce33dfd58d08463cfe3651dbb4 192.168.88.131:7002
18    slots:5795-10922 (5128 slots) master
19    1 additional replica(s)
20 S: 3628651a416fa8e8b8b82eff2608173ca75937a5 192.168.88.131:7005
21    slots: (0 slots) slave
22    replicates 2483a4a01a6cdbce33dfd58d08463cfe3651dbb4
23 S: 5f3f7324507788d072f4c9e13b428cfac5bcfaef 192.168.88.131:7006
24    slots: (0 slots) slave
25    replicates 11e83e152aaa229325db94c4bbb7c846872a94cc
26 S: a0d5b3f18bbb0cb37c0bf92aaca97ac8ba980d10 192.168.88.131:7004
27    slots: (0 slots) slave
28    replicates bd2b45a9395aafd3104291b4fb57a53cc12238db
29 M: 11e83e152aaa229325db94c4bbb7c846872a94cc 192.168.88.131:7003
30    slots:11256-16383 (5128 slots) master
31    1 additional replica(s)
32 [OK] All nodes agree about slots configuration.
33 >>> Check for open slots...
34 >>> Check slots coverage...
35 [OK] All 16384 slots covered.
36 Connecting to node 192.168.88.131:7008: OK
37 >>> Send CLUSTER MEET to node 192.168.88.131:7008 to make it join the cluster.
38 Waiting for the cluster to join.
39 >>> Configure node as replica of 192.168.88.131:7007.
40 [OK] New node added correctly.

 通過客戶端程序查看集群節點信息:cluster nodes 得知 7008 為 7007 的 slave 節點

1 192.168.88.131:7001> cluster nodes
2 bd2b45a9395aafd3104291b4fb57a53cc12238db 192.168.88.131:7001 myself,master - 0 0 1 connected 333-5460
3 9d4b0154bad39acd862d651f0be5d3606e862862 192.168.88.131:7008 slave f4451c0e7d525303897e202cb504f7880b64e4fd 0 1562244812807 11 connected
4 f4451c0e7d525303897e202cb504f7880b64e4fd 192.168.88.131:7007 master - 0 1562244810282 11 connected 0-332 5461-5794 10923-11255
5 2483a4a01a6cdbce33dfd58d08463cfe3651dbb4 192.168.88.131:7002 master - 0 1562244813825 12 connected 5795-10922
6 3628651a416fa8e8b8b82eff2608173ca75937a5 192.168.88.131:7005 slave 2483a4a01a6cdbce33dfd58d08463cfe3651dbb4 0 1562244809775 12 connected
7 5f3f7324507788d072f4c9e13b428cfac5bcfaef 192.168.88.131:7006 slave 11e83e152aaa229325db94c4bbb7c846872a94cc 0 1562244808764 8 connected
8 a0d5b3f18bbb0cb37c0bf92aaca97ac8ba980d10 192.168.88.131:7004 slave bd2b45a9395aafd3104291b4fb57a53cc12238db 0 1562244810786 4 connected
9 11e83e152aaa229325db94c4bbb7c846872a94cc 192.168.88.131:7003 master - 0 1562244811799 8 connected 11256-16383

十、刪除節點


【1】我們將剛添加的 7008 從節點進行刪除,命令如下:刪除后通過 cluster nodes 查看發現 7008 成功移除

 1 [root@learnVM redis-cluster]# ./redis-trib.rb del-node 192.168.88.131:7008 9d4b0154bad39acd862d651f0be5d3606e862862
 2 >>> Removing node 9d4b0154bad39acd862d651f0be5d3606e862862 from cluster 192.168.88.131:7008
 3 Connecting to node 192.168.88.131:7008: OK
 4 Connecting to node 192.168.88.131:7004: OK
 5 Connecting to node 192.168.88.131:7003: OK
 6 Connecting to node 192.168.88.131:7002: OK
 7 Connecting to node 192.168.88.131:7005: OK
 8 Connecting to node 192.168.88.131:7001: OK
 9 Connecting to node 192.168.88.131:7006: OK
10 Connecting to node 192.168.88.131:7007: OK
11 >>> Sending CLUSTER FORGET messages to the cluster...
12 >>> SHUTDOWN the node.

【2】接着主節點 7007 會發現不能刪除,因為其占有 hash 槽,需要將槽分配給其他節點,方能刪除:

 1 [root@learnVM redis-cluster]# ./redis-trib.rb del-node 192.168.88.131:7007 f4451c0e7d525303897e202cb504f7880b64e4fd
 2 >>> Removing node f4451c0e7d525303897e202cb504f7880b64e4fd from cluster 192.168.88.131:7007
 3 Connecting to node 192.168.88.131:7007: OK
 4 Connecting to node 192.168.88.131:7006: OK
 5 Connecting to node 192.168.88.131:7002: OK
 6 Connecting to node 192.168.88.131:7003: OK
 7 Connecting to node 192.168.88.131:7005: OK
 8 Connecting to node 192.168.88.131:7001: OK
 9 Connecting to node 192.168.88.131:7004: OK
10 [ERR] Node 192.168.88.131:7007 is not empty! Reshard data away and try again.

【3】將 7007 的槽分配給 7001(參考 八【3】 的槽重新分配:重點修改內容如下:) 

 1 (ps:通過任意節點連接上集群redis集群)
 2 [root@learnVM redis-cluster]# ./redis-trib.rb reshard 192.168.88.131:7001
 3 (ps:輸入當時分配給7007的槽個數1000)
 4 How many slots do you want to move (from 1 to 16384)? 1000
 5 (ps:輸入要分給的節點ID,這里我輸的是7001的ID)
 6 What is the receiving node ID? bd2b45a9395aafd3104291b4fb57a53cc12238db
 7 (ps:這里輸入槽的來源:輸入7007的ID,之前輸入的是all)
 8 Source node #1:f4451c0e7d525303897e202cb504f7880b64e4fd
 9 (ps:輸入done表示可以開始遷移計划)
10 Source node #2:done
11 (ps:輸入yes表示可以開始執行遷移計划)
12 Do you want to proceed with the proposed reshard plan (yes/no)? yes
13 (ps:可以通過 cluster nodes 查看7007的槽個數了)

【4】重新執行刪除 7007 節點的命令:通過 cluster nodes 會發現 7007 節點以被刪除

 1 [root@learnVM redis-cluster]# ./redis-trib.rb del-node 192.168.88.131:7007 f4451c0e7d525303897e202cb504f7880b64e4fd
 2 >>> Removing node f4451c0e7d525303897e202cb504f7880b64e4fd from cluster 192.168.88.131:7007
 3 Connecting to node 192.168.88.131:7007: OK
 4 Connecting to node 192.168.88.131:7006: OK
 5 Connecting to node 192.168.88.131:7002: OK
 6 Connecting to node 192.168.88.131:7003: OK
 7 Connecting to node 192.168.88.131:7005: OK
 8 Connecting to node 192.168.88.131:7001: OK
 9 Connecting to node 192.168.88.131:7004: OK
10 >>> Sending CLUSTER FORGET messages to the cluster...
11 >>> SHUTDOWN the node.

十一、遷移


RedisCluster 提供了工具 redis-trib 可以讓運維人員手動調整槽位的分配情況。Redis 數據遷移是槽,Reids 是一個一個槽進行遷移,當一個槽正在遷移時,這個槽就處於中間過渡狀態。如下圖:槽的源節點的狀態為 migrating(遷移),在目標節點的狀態為 importin(導入)表示數據正在從源節點流向目標節點。

遷移工具 redis-trib 首先會在源節點和目標節點設置好中間過渡狀態,然后一次性獲取源節點槽位的所有 key 列表(keysinslot 指令,可以部分獲取)再挨個 key 進行遷移。每個 key 的遷移過程是以源節點作為目標節點的“客戶端”源節點對當前的 key 執行 dump 指令得到序列化內容,然后通過“客戶端”向目標節點發送 restore 指令攜帶序列化的內容作為參數,目標節點再進行反序列化就可以將內容恢復到目標節點的內存中,然后返回“客戶端”OK,源節點“客戶端”收到后再把當前節點的 key 刪除掉就完成了單個 key 遷移的全過程。大致流程:從源節點獲取內容—>存在目標節點—>從源節點刪除內容。這里的遷移過程是同步的,在目標節點執行 restore 指令到源節點刪除 key 之間,源節點的主線程會處於阻塞狀態,直到 key 被成功刪除。如果遷移過程中出現網絡故障,整個槽遷移了一半,這時兩個節點依舊處於中間過渡狀態,待下次遷移工具重新連接上時,會提示用戶繼續進行遷移。

遷移過程中,如果每個 key 的內容都很小,migrate 指令會執行得很快,他就不會影響客戶端的正常訪問。如果 key 的內容很大,因為 migrate 指令時阻塞指令,會同時導致源節點和目標節點卡頓,影響集群的穩定性。所以在集群環境下,業務邏輯要盡可能避免產生很大的key

遷移過程中,客戶端訪問流程的變化: 首先新舊兩個節點對應的槽位都存在部分 key 數據。客戶端先嘗試訪問舊節點。如果對應的數據還在舊節點里面,那么舊節點正常處理。那么對應的節點不在舊節點中,那么有兩種可能,不存在或者在新節點中。此時舊節點會向客戶端發送 -ASK targetNodeAddr 的重定向指令。客戶端收到指令后,先去目標節點執行一個不帶任何參數的 ASKING 指令,然后在目標節點再重新執行原先的操作指令

為什么需要執行一個不帶參數的 ASKING 指令:在遷移未完成之前,按理說這個槽位還是不歸新節點管理的,如果這個時候向目標節點發送該槽位的指令,節點是不認的,它會向客戶端返回一個 -MOVED 重定向指令告訴它去源節點執行。如此就會形成重定向循環。ASKING 指令的目標就是打開目標節點的選項,告訴它下一條指令不能不理,要當成自己的槽位來處理。從以上過程可以看出,遷移會影響服務效率的,同樣的指令在正常情況下一個ttl 就能完成,而在遷移情況下需要3個ttl 才能搞定

十二、容錯


Redis Cluster 可以為每個主節點設置若干個從節點,當主節點發生故障時,集群會自動將其中某個從節點提升為主節點。如果某個主節點沒有從節點,那么當它發生故障時,集群將完全處於不可用狀態。不過 Redis 也提供了一個參數 cluster-require-full-converage 可以允許部分節點發生故障,其他節點還可以繼續提供對外訪問。

十三、網絡抖動


網絡抖動:突然間部分連接變得不可訪問,然后又很快恢復正常。為解決這個問題,Redis Cluster 提供了一種選項 cluster-node-timeout,表示當某個節點持續 timeout的時間失聯時,才可以認定該節點出現故障,需要進行主從切換。如果沒有這個選項,網絡抖動導致主從頻繁切換。cluster-slave-validity-factor 配置系數,等於零時,主從切換是不會抗拒網絡抖動的。如果這個系數大於1,他就成了主從切換的松弛系數。
PFail 與 Fail】:只有大多數節點都認定某個節點掛了,集群才認為該節點需要進行主從切換來容錯。Redis 節點采用 Gossip 協議來廣播自己的狀態以及對整個集群的認知。比如一個節點發現某個節點失聯了(PFail:Possibly Fail),它會將這條信息整個集群廣播,其他節點就可以收到這個節點的失聯信息。如果收到了某個節點失聯的節點數量(PFail Count)已經達到了集群的大多數,就可以標記該失聯節點為確定下線狀態(Fail),然后向整個集群廣播,強迫其他節點頁接受該節點已經下線的事實,並立即對該失聯節點進行主從切換。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM