Redis 學習筆記(十五)Redis Cluster 集羣擴容與收縮

Redis Cluster 集羣伸縮

1. 伸縮原理

Redis提供了靈活的節點擴容和收縮方案。在不影響集羣對外服務的情況下,可以爲集羣添加節點進行擴容也可以對下線節點進行縮容。

我們在Redis Cluster 介紹與搭建這篇文章中搭建了一個三主三從Redis集羣(如下圖所示)。在搭建 Redis Cluster 通信流程剖析這篇博客中根據源碼詳細剖析了搭建集羣的流程。

這裏寫圖片描述

本篇博客要講的是,Redis集羣的擴容和縮容過程。

我們先根據Redis Cluster 介紹與搭建將如圖的集羣搭建起來,查看搭建的效果。

127.0.0.1:6379> cluster nodes
29978c0169ecc0a9054de7f4142155c1ab70258b 127.0.0.1:6379 myself,master - 0 0 7 connected 0-5461

8f285670923d4f1c599ecc93367c95a30fb8bf34 127.0.0.1:6380 master - 0 1496717082785 3 connected 5462-10922

66478bda726ae6ba4e8fb55034d8e5e5804223ff 127.0.0.1:6381 master - 0 1496717085793 2 connected 10923-16383

961097d6be64ebd2fd739ff719e97565a8cee7b5 127.0.0.1:6382 slave 29978c0169ecc0a9054de7f4142155c1ab70258b 0 1496717084791 7 connected

6fb7dfdb6188a9fe53c48ea32d541724f36434e9 127.0.0.1:6383 slave 8f285670923d4f1c599ecc93367c95a30fb8bf34 0 1496717087797 4 connected

e0c7961a1b07ab655bc31d8dfd583da565ec167d 127.0.0.1:6384 slave 66478bda726ae6ba4e8fb55034d8e5e5804223ff 0 1496717086795 2 connected

對應的主節點負責的槽位信息,如下圖所示:

這裏寫圖片描述

2. 擴容集羣

擴容集羣是分佈式存儲最常見的需求,Redis集羣擴容可以分爲如下步驟:

  • 準備新節點
  • 加入集羣
  • 遷移槽和數據

2.1 準備新節點

我們需要兩個節點,端口分別爲63856386,配置和之前集羣節點配置基本相同,除了端口不同,以便於管理。6385節點配置如下:

port 6385                               //端口
cluster-enabled yes                     //開啓集羣模式
cluster-config-file nodes-6385.conf     //集羣內部的配置文件
cluster-node-timeout 15000              //節點超時時間,單位毫秒
// 其他配置和單機模式相同

啓動兩個節點

sudo redis-server conf/redis-6385.conf
sudo redis-server conf/redis-6386.conf

啓動後的新節點會作爲孤兒節點運行,沒有和其他節點與之通信。

2.2 加入集羣

我們可以通過CLUSTER MEET命令將6385節點加入到集羣中。

127.0.0.1:6379> CLUSTER MEET 127.0.0.1 6385
OK
127.0.0.1:6379> CLUSTER NODES
cb987394a3acc7a5e606c72e61174b48e437cedb 127.0.0.1:6385 master - 0 1496731333689 8 connected
......

也可以使用redis專門進行集羣管理的工具redis-trib.rb,位於Redis的源碼目錄中,把6386節點加入到集羣中

sudo src/redis-trib.rb add-node 127.0.0.1:6386 127.0.0.1:6379
127.0.0.1:6379> CLUSTER NODES
cdfb1656353c5c7f29d0330a754c71d53cec464c 127.0.0.1:6386 master - 0 1496731447703 0 connected
......

這兩種方法可以,新加入的節點都是主節點,因爲沒有負責槽位,所以不能接受任何讀寫操作,對於新加入的節點,我們可以有兩個操作:

  • 爲新節點遷移槽和數據實現擴容。
  • 作爲其他主節點的從節點負責故障轉移。

2.3 遷移槽和數據

當我們將新節點加入集羣后,我們就可以將槽和數據遷移到新的節點,遷移的方法也有兩種,可以使用redis-trib.rb工具,也可以通過手動命令的方式,但是一般要確保每個主節點負責的槽數是均勻的,因此要使用redis-trib.rb工具來批量完成,但是我們只是爲了演示遷移的過程,所以接下來手動使用命令進行遷移。

我們先創建幾個屬於一個槽的鍵,將這些鍵遷移到新的節點中。

127.0.0.1:6379> SET key:{test}:555 value:test:555
-> Redirected to slot [6918] located at 127.0.0.1:6380
OK
127.0.0.1:6380> SET key:{test}:666 value:test:666
OK
127.0.0.1:6380> SET key:{test}:777 value:test:777
OK
127.0.0.1:6380> CLUSTER KEYSLOT key:{test}:555
(integer) 6918
127.0.0.1:6380> CLUSTER KEYSLOT key:{test}:666
(integer) 6918
127.0.0.1:6380> CLUSTER KEYSLOT key:{test}:777
(integer) 6918

本來在6379節點中創建,但是重定向到了6380節點中,因爲我們常見的鍵根據CRC16算法計算分配到了6918槽中,而這個槽由6380節點負責。

如果鍵的名字中帶有{},那麼計算哈希值時就只計算{}包含的字符串,所以創建的三個鍵屬於一個槽。

計算哈希值的源碼如下:

unsigned int keyHashSlot(char *key, int keylen) {
    int s, e; /* start-end indexes of { and } */
    // 找'{'字符
    for (s = 0; s < keylen; s++)
        if (key[s] == '{') break;
    // 沒有找到"{}",直接計算整個key的哈希值
    if (s == keylen) return crc16(key,keylen) & 0x3FFF;
    // 找到'{',檢查是否有'}'
    for (e = s+1; e < keylen; e++)
        if (key[e] == '}') break;
    // 沒有找到配對的'}',直接計算整個key的哈希值
    if (e == keylen || e == s+1) return crc16(key,keylen) & 0x3FFF;
    // 如果找到了"{}",計算{}中間的哈希值
    return crc16(key+s+1,e-s-1) & 0x3FFF;
}

我們已經獲取了要遷移的槽,是6918。因此,流程如下:

  • 目標6385節點中,將槽6918設置爲導入狀態
127.0.0.1:6385> CLUSTER SETSLOT 6918 importing 8f285670923d4f1c599ecc93367c95a30fb8bf34
OK
// 8f285670923d4f1c599ecc93367c95a30fb8bf34 是 6380 節點的名字

目標6385節點中,查看槽6918導入狀態

127.0.0.1:6385> CLUSTER NODES
cb987394a3acc7a5e606c72e61174b48e437cedb 127.0.0.1:6385 myself,master - 0 0 8 connected [6918-<-8f285670923d4f1c599ecc93367c95a30fb8bf34]
  • 6380節點中,將槽6918設置爲導出狀態
127.0.0.1:6380> CLUSTER SETSLOT 6918 migrating cb987394a3acc7a5e606c72e61174b48e437cedb
OK
// cb987394a3acc7a5e606c72e61174b48e437cedb 是 6385 節點的名字

6380節點中,查看槽6918導出狀態

127.0.0.1:6380> CLUSTER NODES
8f285670923d4f1c599ecc93367c95a30fb8bf34 127.0.0.1:6380 myself,master - 0 0 3 connected 5462-10922 [6918->-cb987394a3acc7a5e606c72e61174b48e437cedb]
  • 批量獲取槽6918中的鍵
127.0.0.1:6380> CLUSTER GETKEYSINSLOT 6918 5
1) "key:{test}:555"
2) "key:{test}:666"
3) "key:{test}:777"

確認一下這三個鍵是否存在於源6380節點。

127.0.0.1:6380> MGET key:{test}:777 key:{test}:666 key:{test}:555
1) "value:test:777"
2) "value:test:666"
3) "value:test:555"
  • 執行migrate命令進行遷移
127.0.0.1:6380> MIGRATE 127.0.0.1 6385 "" 0 1000 keys key:{test}:777 key:{test}:666 key:{test}:555
OK

批量遷移版本的MIGRATE命令是在redis 3.0.6之後加入的,命令參數如下:

MIGRATE host port key dbid timeout [COPY | REPLACE]
MIGRATE host port "" dbid timeout [COPY | REPLACE] KEYS key1 key2 ... keyN
// host port 指定遷移的目的節點地址
// dbid 指定遷移的數據庫id
// timeout 遷移的超時時間
// 如果指定了 COPY 選項,表示不刪除源節點上的key
// 如果指定了 REPLACE 選項,替換目標節點上已存在的key(如果存在)

當遷移完成後,我們在源6380節點查詢這三個鍵,發送回覆了一個ASK錯誤

127.0.0.1:6380> MGET key:{test}:777 key:{test}:666 key:{test}:555
(error) ASK 6918 127.0.0.1:6385

最後,我們只需向任意節點發送CLUSTER SETSLOT <slot> NODE <target_name>命令,將槽指派的信息發送給節點,然後這個節點會將這個指派信息發送至整個集羣。

CLUSTER SETSLOT 6918 node cb987394a3acc7a5e606c72e61174b48e437cedb
// cb987394a3acc7a5e606c72e61174b48e437cedb 是 6385 節點的名字

6381節點執行命令

127.0.0.1:6381> CLUSTER SETSLOT 6918 node cb987394a3acc7a5e606c72e61174b48e437cedb
OK

6379節點查看當前集羣槽指派信息

127.0.0.1:6379> CLUSTER NODES
29978c0169ecc0a9054de7f4142155c1ab70258b 127.0.0.1:6379 myself,master - 0 0 7 connected 0-5461
66478bda726ae6ba4e8fb55034d8e5e5804223ff 127.0.0.1:6381 master - 0 1496736248776 2 connected 10923-16383
cb987394a3acc7a5e606c72e61174b48e437cedb 127.0.0.1:6385 master - 0 1496736244766 10 connected 6918
8f285670923d4f1c599ecc93367c95a30fb8bf34 127.0.0.1:6380 master - 0 1496736247773 3 connected 5462-6917 6919-10922
// 過濾掉從節點和未指派槽的主節點

可以看到6380節點負責的槽變爲5462-6917 6919-10922,而6918已經被6385節點負責了。

  • 添加從節點

開始的時候,我們加入了兩個新節點到集羣中,節點6385已經遷移了槽位和數據作爲主節點,但是該節點還不具有故障轉移的能力。

此時,還需要將6386節點作爲6385節點的從節點,從而保證集羣的高可用。使用cluster replicate <master_id>命令爲主節點添加從節點,集羣模式下不支持slaveof命令。

127.0.0.1:6386> CLUSTER REPLICATE cb987394a3acc7a5e606c72e61174b48e437cedb
OK
127.0.0.1:6386> CLUSTER NODES
cb987394a3acc7a5e606c72e61174b48e437cedb 127.0.0.1:6385 master - 0 1496742992748 10 connected 6918
cdfb1656353c5c7f29d0330a754c71d53cec464c 127.0.0.1:6386 myself,slave cb987394a3acc7a5e606c72e61174b48e437cedb 0 0 0 connected

到此就完成了集羣的擴容。集羣關係如下圖所示:

這裏寫圖片描述

3. 收縮集羣

收縮集羣以爲着縮減規模,需要從集羣中安全下線部分節點。需要考慮兩種情況:

  • 確定下線的節點是否有負責槽,如果是,需要把槽遷移到其他節點,保證節點下線後整個槽節點映射的完整性。
  • 當下線節點不在負責槽或着本身是從節點時,就可以通知集羣內其他節點忘記下線節點,當所有節點忘記該節點後就可以正常關閉。

我們這次使用redis-trib.rb工具來下線遷移槽。流程和擴容集羣非常相似,正好方向相反,將6380變爲目標節點,6385成了源節點。將剛纔新擴容的集羣收縮回去。

./redis-trib.rb reshard 127.0.0.1:6385
>>> Performing Cluster Check (using node 127.0.0.1:6385)
......
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
// 你想遷移多少個槽
How many slots do you want to move (from 1 to 16384)? 1 /*遷移一個槽*/
// 目標節點的id
What is the receiving node ID? 8f285670923d4f1c599ecc93367c95a30fb8bf34 /*輸入目標`6380`節點的id*/
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.
// 輸入要遷移槽的源節點
// all 表示所有節點都是源節點
// done 表示輸入完成
Source node #1:cb987394a3acc7a5e606c72e61174b48e437cedb
Source node #2:done
.....
// 是否立即執行重新分片計劃
Do you want to proceed with the proposed reshard plan (yes/no)? yes
Moving slot 6918 from 127.0.0.1:6385 to 127.0.0.1:6380: ...

查看一下結果:

127.0.0.1:6380> CLUSTER NODES
8f285670923d4f1c599ecc93367c95a30fb8bf34 127.0.0.1:6380 myself,master - 0 0 11 connected 5462-10922
cb987394a3acc7a5e606c72e61174b48e437cedb 127.0.0.1:6385 master - 0 1496744498017 10 connected

6380節點已經接管了6385節點的槽。

最後讓集羣所有的節點忘記下線節點6385。執行CLUSTER FORGET <down_node_id>或者使用工具。

./redis-trib.rb del-node 127.0.0.1:6379 cdfb1656353c5c7f29d0330a754c71d53cec464c
>>> Removing node cdfb1656353c5c7f29d0330a754c71d53cec464c from cluster 127.0.0.1:6379
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
./redis-trib.rb del-node 127.0.0.1:6379 cb987394a3acc7a5e606c72e61174b48e437cedb
>>> Removing node cb987394a3acc7a5e606c72e61174b48e437cedb from cluster 127.0.0.1:6379
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.

注意,先下線從節點,在下線主節點,以免不必要的全量複製操作。對6379節點做忘記下線節點的操作,那麼經過一段時間,集羣中的其他節點也都會忘記。

127.0.0.1:6380> CLUSTER NODES
6fb7dfdb6188a9fe53c48ea32d541724f36434e9 127.0.0.1:6383 slave 8f285670923d4f1c599ecc93367c95a30fb8bf34 0 1496744890808 11 connecte
29978c0169ecc0a9054de7f4142155c1ab70258b 127.0.0.1:6379 master - 0 1496744892814 7 connected 0-5461
66478bda726ae6ba4e8fb55034d8e5e5804223ff 127.0.0.1:6381 master - 0 1496744891810 2 connected 10923-16383
e0c7961a1b07ab655bc31d8dfd583da565ec167d 127.0.0.1:6384 slave 66478bda726ae6ba4e8fb55034d8e5e5804223ff 0 1496744888804 2 connected
8f285670923d4f1c599ecc93367c95a30fb8bf34 127.0.0.1:6380 myself,master - 0 0 11 connected 5462-10922
961097d6be64ebd2fd739ff719e97565a8cee7b5 127.0.0.1:6382 slave 29978c0169ecc0a9054de7f4142155c1ab70258b 0 1496744889805 7 connected

6380端口的主節點已經忘記了下線節點,因此下線節點已經安全的下線。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章