redis cluster管理工具redis-trib.rb詳解

weixin_34391854發表於2018-01-08

redis cluster管理工具redis-trib.rb詳解

來源 http://weizijun.cn/2016/01/08/redis%20cluster%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7redis-trib-rb%E8%AF%A6%E8%A7%A3/

 

概述

redis-trib.rb是redis官方推出的管理redis叢集的工具,整合在redis的原始碼src目錄下,是基於redis提供的叢集命令封裝成簡單、便捷、實用的操作工具。redis-trib.rb是redis作者用ruby完成的。為了看懂redis-trib.rb,我特意花了一個星期學習了ruby,也被ruby的簡潔、明瞭所吸引。ruby是門非常靈活的語言,redis-trib.rb只用了1600行左右的程式碼,就實現了強大的叢集操作。本文對redis-trib.rb的介紹是基於redis 3.0.6版本的原始碼。閱讀本文需要對redis叢集功能有一定的瞭解。關於redis叢集功能的介紹,可以參考本人的另一篇文章《redis3.0 cluster功能介紹》

先從redis-trib.rb的help資訊,看下redis-trib.rb提供了哪些功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
$ruby redis-trib.rb help
Usage: redis-trib <command> <options> <arguments ...>

create host1:port1 ... hostN:portN
--replicas <arg>
check host:port
info host:port
fix host:port
--timeout <arg>
reshard host:port
--from <arg>
--to <arg>
--slots <arg>
--yes
--timeout <arg>
--pipeline <arg>
rebalance host:port
--weight <arg>
--auto-weights
--threshold <arg>
--use-empty-masters
--timeout <arg>
--simulate
--pipeline <arg>
add-node new_host:new_port existing_host:existing_port
--slave
--master-id <arg>
del-node host:port node_id
set-timeout host:port milliseconds
call host:port command arg arg .. arg
import host:port
--from <arg>
--copy
--replace
help (show this help)

For check, fix, reshard, del-node, set-timeout you can specify the host and port of any working node in the cluster.

可以看到redis-trib.rb具有以下功能:

  • 1、create:建立叢集
  • 2、check:檢查叢集
  • 3、info:檢視叢集資訊
  • 4、fix:修復叢集
  • 5、reshard:線上遷移slot
  • 6、rebalance:平衡叢集節點slot數量
  • 7、add-node:將新節點加入叢集
  • 8、del-node:從叢集中刪除節點
  • 9、set-timeout:設定叢集節點間心跳連線的超時時間
  • 10、call:在叢集全部節點上執行命令
  • 11、import:將外部redis資料匯入叢集

下面從redis-trib.rb使用和原始碼的角度詳細介紹redis-trib.rb的每個功能。

redis-trib.rb主要有兩個類:ClusterNodeRedisTribClusterNode儲存了每個節點的資訊,RedisTrib則是redis-trib.rb各個功能的實現。

ClusterNode物件

先分析ClusterNode原始碼。ClusterNode有下面幾個成員變數(ruby的類成員變數是以@開頭的):

  • @r:執行redis命令的客戶端物件。
  • @info:儲存了該節點的詳細資訊,包括cluster nodes命令中自己這行的資訊和cluster info的資訊。
  • @dirty:節點資訊是否需要更新,如果為true,我們需要把記憶體的節點更新資訊到節點上。
  • @friends:儲存了叢集其他節點的info資訊。其資訊為通過cluster nodes命令獲得的其他節點資訊。

ClusterNode有下面一些成員方法:

  • initialize:ClusterNode的構造方法,需要傳入節點的地址資訊。
  • friends:返回@friends物件。
  • slots:返回該節點負責的slots資訊。
  • has_flag?:判斷節點info資訊的的flags中是否有給定的flag。
  • to_s:類似java的toString方法,返回節點的地址資訊。
  • connect:連線redis節點。
  • assert_cluster:判斷節點開啟了叢集配置。
  • assert_empty:確定節點目前沒有跟任何其他節點握手,同時自己的db資料為空。
  • load_info:通過cluster info和cluster nodes匯入節點資訊。
  • add_slots:給節點增加slot,該操作只是在記憶體中修改,並把dirty設定成true,等待flush_node_config將記憶體中的資料同步在節點執行。
  • set_as_replica:slave設定複製的master地址。dirty設定成true。
  • flush_node_config:將記憶體的資料修改同步在叢集節點中執行。
  • info_string:簡單的info資訊。
  • get_config_signature:用來驗證叢集節點見的cluster nodes資訊是否一致。該方法返回節點的簽名資訊。
  • info:返回@info物件,包含詳細的info資訊。
  • is_dirty?:判斷@dirty。
  • r:返回執行redis命令的客戶端物件。

有了ClusterNode物件,在處理叢集操作的時候,就獲得了叢集的資訊,可以進行叢集相關操作。在此先簡單介紹下redis-trib.rb指令碼的使用,以create為例:

create host1:port1 ... hostN:portN
       --replicas <arg>

host1:port1 ... hostN:portN表示子引數,這個必須在可選引數之後,--replicas <arg>是可選引數,帶的表示後面必須填寫一個引數,像--slave這樣,後面就不帶引數,掌握了這個基本規則,就能從help命令中獲得redis-trib.rb的使用方法。

其他命令大都需要傳遞host:port,這是redis-trib.rb為了連線叢集,需要選擇叢集中的一個節點,然後通過該節點獲得整個叢集的資訊。

下面就一一詳細介紹redis-trib.rb的每個功能。

create建立叢集

create命令可選replicas引數,replicas表示需要有幾個slave。最簡單命令使用如下:

$ruby redis-trib.rb create 10.180.157.199:6379 10.180.157.200:6379 10.180.157.201:6379

有一個slave的建立命令如下:

$ruby redis-trib.rb create --replicas 1 10.180.157.199:6379 10.180.157.200:6379 10.180.157.201:6379 10.180.157.202:6379  10.180.157.205:6379  10.180.157.208:6379 

建立流程如下:

  • 1、首先為每個節點建立ClusterNode物件,包括連線每個節點。檢查每個節點是否為獨立且db為空的節點。執行load_info方法匯入節點資訊。
  • 2、檢查傳入的master節點數量是否大於等於3個。只有大於3個節點才能組成叢集。
  • 3、計算每個master需要分配的slot數量,以及給master分配slave。分配的演算法大致如下:
    • 先把節點按照host分類,這樣保證master節點能分配到更多的主機中。
    • 不停遍歷遍歷host列表,從每個host列表中彈出一個節點,放入interleaved陣列。直到所有的節點都彈出為止。
    • master節點列表就是interleaved前面的master數量的節點列表。儲存在masters陣列。
    • 計算每個master節點負責的slot數量,儲存在slots_per_node物件,用slot總數除以master數量取整即可。
    • 遍歷masters陣列,每個master分配slots_per_node個slot,最後一個master,分配到16384個slot為止。
    • 接下來為master分配slave,分配演算法會盡量保證master和slave節點不在同一臺主機上。對於分配完指定slave數量的節點,還有多餘的節點,也會為這些節點尋找master。分配演算法會遍歷兩次masters陣列。
    • 第一次遍歷masters陣列,在餘下的節點列表找到replicas數量個slave。每個slave為第一個和master節點host不一樣的節點,如果沒有不一樣的節點,則直接取出餘下列表的第一個節點。
    • 第二次遍歷是在對於節點數除以replicas不為整數,則會多餘一部分節點。遍歷的方式跟第一次一樣,只是第一次會一次性給master分配replicas數量個slave,而第二次遍歷只分配一個,直到餘下的節點被全部分配出去。
  • 4、列印出分配資訊,並提示使用者輸入“yes”確認是否按照列印出來的分配方式建立叢集。
  • 5、輸入“yes”後,會執行flush_nodes_config操作,該操作執行前面的分配結果,給master分配slot,讓slave複製master,對於還沒有握手(cluster meet)的節點,slave複製操作無法完成,不過沒關係,flush_nodes_config操作出現異常會很快返回,後續握手後會再次執行flush_nodes_config。
  • 6、給每個節點分配epoch,遍歷節點,每個節點分配的epoch比之前節點大1。
  • 7、節點間開始相互握手,握手的方式為節點列表的其他節點跟第一個節點握手。
  • 8、然後每隔1秒檢查一次各個節點是否已經訊息同步完成,使用ClusterNode的get_config_signature方法,檢查的演算法為獲取每個節點cluster nodes資訊,排序每個節點,組裝成node_id1:slots|node_id2:slot2|...的字串。如果每個節點獲得字串都相同,即認為握手成功。
  • 9、此後會再執行一次flush_nodes_config,這次主要是為了完成slave複製操作。
  • 10、最後再執行check_cluster,全面檢查一次叢集狀態。包括和前面握手時檢查一樣的方式再檢查一遍。確認沒有遷移的節點。確認所有的slot都被分配出去了。
  • 11、至此完成了整個建立流程,返回[OK] All 16384 slots covered.

check檢查叢集

檢查叢集狀態的命令,沒有其他引數,只需要選擇一個叢集中的一個節點即可。執行命令以及結果如下:

$ruby redis-trib.rb check 10.180.157.199:6379
>>> Performing Cluster Check (using node 10.180.157.199:6379)
M: b2506515b38e6bbd3034d540599f4cd2a5279ad1 10.180.157.199:6379
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
S: d376aaf80de0e01dde1f8cd4647d5ac3317a8641 10.180.157.205:6379
   slots: (0 slots) slave
   replicates e36c46dbe90960f30861af00786d4c2064e63df2
M: 15126fb33796c2c26ea89e553418946f7443d5a5 10.180.157.201:6379
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 59fa6ee455f58a5076f6d6f83ddd74161fd7fb55 10.180.157.208:6379
   slots: (0 slots) slave
   replicates 15126fb33796c2c26ea89e553418946f7443d5a5
S: 460b3a11e296aafb2615043291b7dd98274bb351 10.180.157.202:6379
   slots: (0 slots) slave
   replicates b2506515b38e6bbd3034d540599f4cd2a5279ad1
M: e36c46dbe90960f30861af00786d4c2064e63df2 10.180.157.200: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.    

檢查前會先執行load_cluster_info_from_node方法,把所有節點資料load進來。load的方式為通過自己的cluster nodes發現其他節點,然後連線每個節點,並加入nodes陣列。接著生成節點間的複製關係。

load完資料後,開始檢查資料,檢查的方式也是呼叫建立時候使用的check_cluster。

info檢視叢集資訊

info命令用來檢視叢集的資訊。info命令也是先執行load_cluster_info_from_node獲取完整的叢集資訊。然後顯示ClusterNode的info_string結果,示例如下:

$ruby redis-trib.rb info 10.180.157.199:6379
10.180.157.199:6379 (b2506515...) -> 0 keys | 5461 slots | 1 slaves.
10.180.157.201:6379 (15126fb3...) -> 0 keys | 5461 slots | 1 slaves.
10.180.157.200:6379 (e36c46db...) -> 0 keys | 5462 slots | 1 slaves.
[OK] 0 keys in 3 masters.
0.00 keys per slot on average.

fix修復叢集

fix命令的流程跟check的流程很像,顯示載入叢集資訊,然後在check_cluster方法內傳入fix為
true的變數,會在叢集檢查出現異常的時候執行修復流程。目前fix命令能修復兩種異常,一種是叢集有處於遷移中的slot的節點,一種是slot未完全分配的異常。

fix_open_slot方法是修復叢集有處於遷移中的slot的節點異常。

  • 1、先檢查該slot是誰負責的,遷移的源節點如果沒完成遷移,owner還是該節點。沒有owner的slot無法完成修復功能。
  • 2、遍歷每個節點,獲取哪些節點標記該slot為migrating狀態,哪些節點標記該slot為importing狀態。對於owner不是該節點,但是通過cluster countkeysinslot獲取到該節點有資料的情況,也認為該節點為importing狀態。
  • 3、如果migrating和importing狀態的節點均只有1個,這可能是遷移過程中redis-trib.rb被中斷所致,直接執行move_slot繼續完成遷移任務即可。傳遞dots和fix為true。
  • 4、如果migrating為空,importing狀態的節點大於0,那麼這種情況執行回滾流程,將importing狀態的節點資料通過move_slot方法導給slot的owner節點,傳遞dots、fix和cold為true。接著對importing的節點執行cluster stable命令恢復穩定。
  • 5、如果importing狀態的節點為空,有一個migrating狀態的節點,而且該節點在當前slot沒有資料,那麼可以直接把這個slot設為stable。
  • 6、如果migrating和importing狀態不是上述情況,目前redis-trib.rb工具無法修復,上述的三種情況也已經覆蓋了通過redis-trib.rb工具遷移出現異常的各個方面,人為的異常情形太多,很難考慮完全。

fix_slots_coverage方法能修復slot未完全分配的異常。未分配的slot有三種狀態。

  • 1、所有節點的該slot都沒有資料。該狀態redis-trib.rb工具直接採用隨機分配的方式,並沒有考慮節點的均衡。本人嘗試對沒有分配slot的叢集通過fix修復叢集,結果slot還是能比較平均的分配,但是沒有了連續性,列印的slot資訊非常離散。
  • 2、有一個節點的該slot有資料。該狀態下,直接把slot分配給該slot有資料的節點。
  • 3、有多個節點的該slot有資料。此種情況目前還處於TODO狀態,不過redis作者列出了修復的步驟,對這些節點,除第一個節點,執行cluster migrating命令,然後把這些節點的資料遷移到第一個節點上。清除migrating狀態,然後把slot分配給第一個節點。

reshard線上遷移slot

reshard命令可以線上把叢集的一些slot從叢集原來slot負責節點遷移到新的節點,利用reshard可以完成叢集的線上橫向擴容和縮容。

reshard的引數很多,下面來一一解釋一番:

reshard         host:port
                --from <arg>
                --to <arg>
                --slots <arg>
                --yes
                --timeout <arg>
                --pipeline <arg>
  • host:port:這個是必傳引數,用來從一個節點獲取整個叢集資訊,相當於獲取叢集資訊的入口。
  • --from <arg>:需要從哪些源節點上遷移slot,可從多個源節點完成遷移,以逗號隔開,傳遞的是節點的node id,還可以直接傳遞--from all,這樣源節點就是叢集的所有節點,不傳遞該引數的話,則會在遷移過程中提示使用者輸入。
  • --to <arg>:slot需要遷移的目的節點的node id,目的節點只能填寫一個,不傳遞該引數的話,則會在遷移過程中提示使用者輸入。
  • --slots <arg>:需要遷移的slot數量,不傳遞該引數的話,則會在遷移過程中提示使用者輸入。
  • --yes:設定該引數,可以在列印執行reshard計劃的時候,提示使用者輸入yes確認後再執行reshard。
  • --timeout <arg>:設定migrate命令的超時時間。
  • --pipeline <arg>:定義cluster getkeysinslot命令一次取出的key數量,不傳的話使用預設值為10。

遷移的流程如下:

  • 1、通過load_cluster_info_from_node方法裝載叢集資訊。
  • 2、執行check_cluster方法檢查叢集是否健康。只有健康的叢集才能進行遷移。
  • 3、獲取需要遷移的slot數量,使用者沒傳遞--slots引數,則提示使用者手動輸入。
  • 4、獲取遷移的目的節點,使用者沒傳遞--to引數,則提示使用者手動輸入。此處會檢查目的節點必須為master節點。
  • 5、獲取遷移的源節點,使用者沒傳遞--from引數,則提示使用者手動輸入。此處會檢查源節點必須為master節點。--from all的話,源節點就是除了目的節點外的全部master節點。這裡為了保證叢集slot分配的平均,建議傳遞--from all
  • 6、執行compute_reshard_table方法,計算需要遷移的slot數量如何分配到源節點列表,採用的演算法是按照節點負責slot數量由多到少排序,計算每個節點需要遷移的slot的方法為:遷移slot數量 * (該源節點負責的slot數量 / 源節點列表負責的slot總數)。這樣算出的數量可能不為整數,這裡程式碼用了下面的方式處理:

    n = (numslots/source_tot_slots*s.slots.length)
    if i == 0
        n = n.ceil
    else
        n = n.floor
    

    這樣的處理方式會帶來最終分配的slot與請求遷移的slot數量不一致,這個BUG已經在github上提給作者,https://github.com/antirez/redis/issues/2990

  • 7、列印出reshard計劃,如果使用者沒傳--yes,就提示使用者確認計劃。
  • 8、根據reshard計劃,一個個slot的遷移到新節點上,遷移使用move_slot方法,該方法被很多命令使用,具體可以參見下面的遷移流程。move_slot方法傳遞dots為true和pipeline數量。
  • 9、至此,就完成了全部的遷移任務。

下面看下一次reshard的執行結果:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
$ruby redis-trib.rb reshard --from all --to 80b661ecca260c89e3d8ea9b98f77edaeef43dcd --slots 11 10.180.157.199:6379
>>> Performing Cluster Check (using node 10.180.157.199:6379)
S: b2506515b38e6bbd3034d540599f4cd2a5279ad1 10.180.157.199:6379
slots: (0 slots) slave
replicates 460b3a11e296aafb2615043291b7dd98274bb351
S: d376aaf80de0e01dde1f8cd4647d5ac3317a8641 10.180.157.205:6379
slots: (0 slots) slave
replicates e36c46dbe90960f30861af00786d4c2064e63df2
M: 15126fb33796c2c26ea89e553418946f7443d5a5 10.180.157.201:6379
slots:10923-16383 (5461 slots) master
1 additional replica(s)
S: 59fa6ee455f58a5076f6d6f83ddd74161fd7fb55 10.180.157.208:6379
slots: (0 slots) slave
replicates 15126fb33796c2c26ea89e553418946f7443d5a5
M: 460b3a11e296aafb2615043291b7dd98274bb351 10.180.157.202:6379
slots:0-5460 (5461 slots) master
1 additional replica(s)
M: 80b661ecca260c89e3d8ea9b98f77edaeef43dcd 10.180.157.200:6380
slots: (0 slots) master
0 additional replica(s)
M: e36c46dbe90960f30861af00786d4c2064e63df2 10.180.157.200: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.

Ready to move 11 slots.
Source nodes:
M: 15126fb33796c2c26ea89e553418946f7443d5a5 10.180.157.201:6379
slots:10923-16383 (5461 slots) master
1 additional replica(s)
M: 460b3a11e296aafb2615043291b7dd98274bb351 10.180.157.202:6379
slots:0-5460 (5461 slots) master
1 additional replica(s)
M: e36c46dbe90960f30861af00786d4c2064e63df2 10.180.157.200:6379
slots:5461-10922 (5462 slots) master
1 additional replica(s)
Destination node:
M: 80b661ecca260c89e3d8ea9b98f77edaeef43dcd 10.180.157.200:6380
slots: (0 slots) master
0 additional replica(s)
Resharding plan:
Moving slot 5461 from e36c46dbe90960f30861af00786d4c2064e63df2
Moving slot 5462 from e36c46dbe90960f30861af00786d4c2064e63df2
Moving slot 5463 from e36c46dbe90960f30861af00786d4c2064e63df2
Moving slot 5464 from e36c46dbe90960f30861af00786d4c2064e63df2
Moving slot 0 from 460b3a11e296aafb2615043291b7dd98274bb351
Moving slot 1 from 460b3a11e296aafb2615043291b7dd98274bb351
Moving slot 2 from 460b3a11e296aafb2615043291b7dd98274bb351
Moving slot 10923 from 15126fb33796c2c26ea89e553418946f7443d5a5
Moving slot 10924 from 15126fb33796c2c26ea89e553418946f7443d5a5
Moving slot 10925 from 15126fb33796c2c26ea89e553418946f7443d5a5
Do you want to proceed with the proposed reshard plan (yes/no)? yes
Moving slot 5461 from 10.180.157.200:6379 to 10.180.157.200:6380:
Moving slot 5462 from 10.180.157.200:6379 to 10.180.157.200:6380:
Moving slot 5463 from 10.180.157.200:6379 to 10.180.157.200:6380:
Moving slot 5464 from 10.180.157.200:6379 to 10.180.157.200:6380:
Moving slot 0 from 10.180.157.202:6379 to 10.180.157.200:6380:
Moving slot 1 from 10.180.157.202:6379 to 10.180.157.200:6380:
Moving slot 2 from 10.180.157.202:6379 to 10.180.157.200:6380:
Moving slot 10923 from 10.180.157.201:6379 to 10.180.157.200:6380:
Moving slot 10924 from 10.180.157.201:6379 to 10.180.157.200:6380:
Moving slot 10925 from 10.180.157.201:6379 to 10.180.157.200:6380:

move_slot方法可以線上將一個slot的全部資料從源節點遷移到目的節點,fix、reshard、rebalance都需要呼叫該方法遷移slot。

move_slot接受下面幾個引數,

  • 1、pipeline:設定一次從slot上獲取多少個key。
  • 2、quiet:遷移會列印相關資訊,設定quiet引數,可以不用列印這些資訊。
  • 3、cold:設定cold,會忽略執行importing和migrating。
  • 4、dots:設定dots,則會在遷移過程列印遷移key數量的進度。
  • 5、update:設定update,則會更新記憶體資訊,方便以後的操作。

move_slot流程如下:

  • 1、如果沒有設定cold,則對源節點執行cluster importing命令,對目的節點執行migrating命令。fix的時候有可能importing和migrating已經執行過來,所以此種場景會設定cold。
  • 2、通過cluster getkeysinslot命令,一次性獲取遠節點遷移slot的pipeline個key的數量.
  • 3、對這些key執行migrate命令,將資料從源節點遷移到目的節點。
  • 4、如果migrate出現異常,在fix模式下,BUSYKEY的異常,會使用migrate的replace模式再執行一次,BUSYKEY表示目的節點已經有該key了,replace模式可以強制替換目的節點的key。不是fix模式就直接返回錯誤了。
  • 5、迴圈執行cluster getkeysinslot命令,直到返回的key數量為0,就退出迴圈。
  • 6、如果沒有設定cold,對每個節點執行cluster setslot命令,把slot賦給目的節點。
  • 7、如果設定update,則修改源節點和目的節點的slot資訊。
  • 8、至此完成了遷移slot的流程。

rebalance平衡叢集節點slot數量

rebalance命令可以根據使用者傳入的引數平衡叢集節點的slot數量,rebalance功能非常強大,可以傳入的引數很多,以下是rebalance的引數列表和命令示例。

rebalance       host:port
                --weight <arg>
                --auto-weights
                --threshold <arg>
                --use-empty-masters
                --timeout <arg>
                --simulate
                --pipeline <arg>

$ruby redis-trib.rb rebalance --threshold 1 --weight b31e3a2e=5 --weight 60b8e3a1=5 --use-empty-masters  --simulate 10.180.157.199:6379

下面也先一一解釋下每個引數的用法:

  • host:port:這個是必傳引數,用來從一個節點獲取整個叢集資訊,相當於獲取叢集資訊的入口。
  • --weight <arg>:節點的權重,格式為node_id=weight,如果需要為多個節點分配權重的話,需要新增多個--weight <arg>引數,即--weight b31e3a2e=5 --weight 60b8e3a1=5,node_id可為節點名稱的字首,只要保證字首位數能唯一區分該節點即可。沒有傳遞–weight的節點的權重預設為1。
  • --auto-weights:這個引數在rebalance流程中並未用到。
  • --threshold <arg>:只有節點需要遷移的slot閾值超過threshold,才會執行rebalance操作。具體計算方法可以參考下面的rebalance命令流程的第四步。
  • --use-empty-masters:rebalance是否考慮沒有節點的master,預設沒有分配slot節點的master是不參與rebalance的,設定--use-empty-masters可以讓沒有分配slot的節點參與rebalance。
  • --timeout <arg>:設定migrate命令的超時時間。
  • --simulate:設定該引數,可以模擬rebalance操作,提示使用者會遷移哪些slots,而不會真正執行遷移操作。
  • --pipeline <arg>:與reshar的pipeline引數一樣,定義cluster getkeysinslot命令一次取出的key數量,不傳的話使用預設值為10。

rebalance命令流程如下:

  • 1、load_cluster_info_from_node方法先載入叢集資訊。
  • 2、計算每個master的權重,根據引數--weight <arg>,為每個設定的節點分配權重,沒有設定的節點,則權重預設為1。
  • 3、根據每個master的權重,以及總的權重,計算自己期望被分配多少個slot。計算的方式為:總slot數量 * (自己的權重 / 總權重)。
  • 4、計算每個master期望分配的slot是否超過設定的閾值,即--threshold <arg>設定的閾值或者預設的閾值。計算的方式為:先計算期望移動節點的閾值,演算法為:(100-(100.0*expected/n.slots.length)).abs,如果計算出的閾值沒有超出設定閾值,則不需要為該節點移動slot。只要有一個master的移動節點超過閾值,就會觸發rebalance操作。
  • 5、如果觸發了rebalance操作。那麼就開始執行rebalance操作,先將每個節點當前分配的slots數量減去期望分配的slot數量獲得balance值。將每個節點的balance從小到大進行排序獲得sn陣列。
  • 6、用dst_idx和src_idx遊標分別從sn陣列的頭部和尾部開始遍歷。目的是為了把尾部節點的slot分配給頭部節點。

    sn陣列儲存的balance列表排序後,負數在前面,正數在後面。負數表示需要有slot遷入,所以使用dst_idx遊標,正數表示需要有slot遷出,所以使用src_idx遊標。理論上sn陣列各節點的balance值加起來應該為0,不過由於在計算期望分配的slot的時候只是使用直接取整的方式,所以可能出現balance值之和不為0的情況,balance值之和不為0即為節點不平衡的slot數量,由於slot總數有16384個,不平衡數量相對於總數,基數很小,所以對rebalance流程影響不大。

  • 7、獲取sn[dst_idx]和sn[src_idx]的balance值較小的那個值,該值即為需要從sn[src_idx]節點遷移到sn[dst_idx]節點的slot數量。
  • 8、接著通過compute_reshard_table方法計算源節點的slot如何分配到源節點列表。這個方法在reshard流程中也有呼叫,具體步驟可以參考reshard流程的第六步。
  • 9、如果是simulate模式,則只是列印出遷移列表。
  • 10、如果沒有設定simulate,則執行move_slot操作,遷移slot,傳入的引數為:quiet=>true,:dots=>false,:update=>true。
  • 11、遷移完成後更新sn[dst_idx]和sn[src_idx]的balance值。如果balance值為0後,遊標向前進1。
  • 12、直到dst_idx到達src_idx遊標,完成整個rebalance操作。

add-node將新節點加入叢集

add-node命令可以將新節點加入叢集,節點可以為master,也可以為某個master節點的slave。

add-node    new_host:new_port existing_host:existing_port
          --slave
          --master-id <arg>

add-node有兩個可選引數:

  • --slave:設定該引數,則新節點以slave的角色加入叢集
  • --master-id:這個引數需要設定了--slave才能生效,--master-id用來指定新節點的master節點。如果不設定該引數,則會隨機為節點選擇master節點。

可以看下add-node命令的執行示例:

$ruby redis-trib.rb add-node --slave --master-id dcb792b3e85726f012e83061bf237072dfc45f99 10.180.157.202:6379 10.180.157.199:6379
>>> Adding node 10.180.157.202:6379 to cluster 10.180.157.199:6379
>>> Performing Cluster Check (using node 10.180.157.199:6379)
M: dcb792b3e85726f012e83061bf237072dfc45f99 10.180.157.199:6379
   slots:0-5460 (5461 slots) master
   0 additional replica(s)
M: 464d740bf48953ebcf826f4113c86f9db3a9baf3 10.180.157.201:6379
   slots:10923-16383 (5461 slots) master
   0 additional replica(s)
M: befa7e17b4e5f239e519bc74bfef3264a40f96ae 10.180.157.200:6379
   slots:5461-10922 (5462 slots) master
   0 additional replica(s)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
>>> Send CLUSTER MEET to node 10.180.157.202:6379 to make it join the cluster.
Waiting for the cluster to join.
>>> Configure node as replica of 10.180.157.199:6379.
[OK] New node added correctly.

add-node流程如下:

  • 1、通過load_cluster_info_from_node方法轉載叢集資訊,check_cluster方法檢查叢集是否健康。
  • 2、如果設定了--slave,則需要為該節點尋找master節點。設定了--master-id,則以該節點作為新節點的master,如果沒有設定--master-id,則呼叫get_master_with_least_replicas方法,尋找slave數量最少的master節點。如果slave數量一致,則選取load_cluster_info_from_node順序發現的第一個節點。load_cluster_info_from_node順序的第一個節點是add-node設定的existing_host:existing_port節點,後面的順序根據在該節點執行cluster nodes返回的結果返回的節點順序。
  • 3、連線新的節點並與叢集第一個節點握手。
  • 4、如果沒設定–slave就直接返回ok,設定了–slave,則需要等待確認新節點加入叢集,然後執行cluster replicate命令複製master節點。
  • 5、至此,完成了全部的增加節點的流程。

del-node從叢集中刪除節點

del-node可以把某個節點從叢集中刪除。del-node只能刪除沒有分配slot的節點。刪除命令傳遞兩個引數:

  • host:port:從該節點獲取叢集資訊。
  • node_id:需要刪除的節點id。

del-node執行結果示例如下:

$ruby redis-trib.rb del-node 10.180.157.199:6379 d5f6d1d17426bd564a6e309f32d0f5b96962fe53
>>> Removing node d5f6d1d17426bd564a6e309f32d0f5b96962fe53 from cluster 10.180.157.199:6379
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.

del-node流程如下:

  • 1、通過load_cluster_info_from_node方法轉載叢集資訊。
  • 2、根據傳入的node id獲取節點,如果節點沒找到,則直接提示錯誤並退出。
  • 3、如果節點分配的slot不為空,則直接提示錯誤並退出。
  • 4、遍歷叢集內的其他節點,執行cluster forget命令,從每個節點中去除該節點。如果刪除的節點是master,而且它有slave的話,這些slave會去複製其他master,呼叫的方法是get_master_with_least_replicas,與add-node沒設定--master-id尋找master的方法一樣。
  • 5、然後關閉該節點。

set-timeout設定叢集節點間心跳連線的超時時間

set-timeout用來設定叢集節點間心跳連線的超時時間,單位是毫秒,不得小於100毫秒,因為100毫秒對於心跳時間來說太短了。該命令修改是節點配置引數cluster-node-timeout,預設是15000毫秒。通過該命令,可以給每個節點設定超時時間,設定的方式使用config set命令動態設定,然後執行config rewrite命令將配置持久化儲存到硬碟。以下是示例:

ruby redis-trib.rb set-timeout 10.180.157.199:6379 30000
>>> Reconfiguring node timeout in every cluster node...
*** New timeout set for 10.180.157.199:6379
*** New timeout set for 10.180.157.205:6379
*** New timeout set for 10.180.157.201:6379
*** New timeout set for 10.180.157.200:6379
*** New timeout set for 10.180.157.208:6379
>>> New node timeout set. 5 OK, 0 ERR.

call在叢集全部節點上執行命令

call命令可以用來在叢集的全部節點執行相同的命令。call命令也是需要通過叢集的一個節點地址,連上整個叢集,然後在叢集的每個節點執行該命令。

$ruby redis-trib.rb call 10.180.157.199:6379 get key
>>> Calling GET key
10.180.157.199:6379: MOVED 12539 10.180.157.201:6379
10.180.157.205:6379: MOVED 12539 10.180.157.201:6379
10.180.157.201:6379:
10.180.157.200:6379: MOVED 12539 10.180.157.201:6379
10.180.157.208:6379: MOVED 12539 10.180.157.201:6379

import將外部redis資料匯入叢集

import命令可以把外部的redis節點資料匯入叢集。匯入的流程如下:

  • 1、通過load_cluster_info_from_node方法轉載叢集資訊,check_cluster方法檢查叢集是否健康。
  • 2、連線外部redis節點,如果外部節點開啟了cluster_enabled,則提示錯誤。
  • 3、通過scan命令遍歷外部節點,一次獲取1000條資料。
  • 4、遍歷這些key,計算出key對應的slot。
  • 5、執行migrate命令,源節點是外部節點,目的節點是叢集slot對應的節點,如果設定了--copy引數,則傳遞copy引數,如果設定了--replace,則傳遞replace引數。
  • 6、不停執行scan命令,直到遍歷完全部的key。
  • 7、至此完成整個遷移流程

這中間如果出現異常,程式就會停止。沒使用--copy模式,則可以重新執行import命令,使用--copy的話,最好清空新的叢集再匯入一次。

import命令更適合離線的把外部redis資料匯入,線上匯入的話最好使用更專業的匯入工具,以slave的方式連線redis節點去同步節點資料應該是更好的方式。

 

相關文章