來源 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/java
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功能介紹》。node
先從redis-trib.rb的help信息,看下redis-trib.rb提供了哪些功能。git
1 |
$ruby redis-trib.rb help |
能夠看到redis-trib.rb具備如下功能:github
create
:建立集羣check
:檢查集羣info
:查看集羣信息fix
:修復集羣reshard
:在線遷移slotrebalance
:平衡集羣節點slot數量add-node
:將新節點加入集羣del-node
:從集羣中刪除節點set-timeout
:設置集羣節點間心跳鏈接的超時時間call
:在集羣所有節點上執行命令import
:將外部redis數據導入集羣下面從redis-trib.rb使用和源碼的角度詳細介紹redis-trib.rb的每一個功能。redis
redis-trib.rb主要有兩個類:ClusterNode
和RedisTrib
。ClusterNode
保存了每一個節點的信息,RedisTrib
則是redis-trib.rb各個功能的實現。算法
先分析ClusterNode
源碼。ClusterNode
有下面幾個成員變量(ruby的類成員變量是以@開頭的):數組
@r
:執行redis命令的客戶端對象。@info
:保存了該節點的詳細信息,包括cluster nodes命令中本身這行的信息和cluster info的信息。@dirty
:節點信息是否須要更新,若是爲true,咱們須要把內存的節點更新信息到節點上。@friends
:保存了集羣其餘節點的info信息。其信息爲經過cluster nodes命令得到的其餘節點信息。ClusterNode
有下面一些成員方法:ruby
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命令可選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
建立流程以下:
load_info
方法導入節點信息。node_id1:slots|node_id2:slot2|...
的字符串。若是每一個節點得到字符串都相同,即認爲握手成功。[OK] All 16384 slots covered.
。檢查集羣狀態的命令,沒有其餘參數,只須要選擇一個集羣中的一個節點便可。執行命令以及結果以下:
$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命令也是先執行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命令的流程跟check的流程很像,顯示加載集羣信息,而後在check_cluster方法內傳入fix爲
true的變量,會在集羣檢查出現異常的時候執行修復流程。目前fix命令能修復兩種異常,一種是集羣有處於遷移中的slot的節點,一種是slot未徹底分配的異常。
fix_open_slot方法是修復集羣有處於遷移中的slot的節點異常。
cluster countkeysinslot
獲取到該節點有數據的狀況,也認爲該節點爲importing狀態。cluster stable
命令恢復穩定。fix_slots_coverage方法能修復slot未徹底分配的異常。未分配的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。遷移的流程以下:
--slots
參數,則提示用戶手動輸入。--to
參數,則提示用戶手動輸入。此處會檢查目的節點必須爲master節點。--from
參數,則提示用戶手動輸入。此處會檢查源節點必須爲master節點。--from all
的話,源節點就是除了目的節點外的所有master節點。這裏爲了保證集羣slot分配的平均,建議傳遞--from all
。六、執行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。
--yes
,就提示用戶確認計劃。下面看下一次reshard的執行結果:
1 |
$ruby redis-trib.rb reshard --from all --to 80b661ecca260c89e3d8ea9b98f77edaeef43dcd --slots 11 10.180.157.199:6379 |
move_slot方法能夠在線將一個slot的所有數據從源節點遷移到目的節點,fix、reshard、rebalance都須要調用該方法遷移slot。
move_slot接受下面幾個參數,
pipeline
:設置一次從slot上獲取多少個key。quiet
:遷移會打印相關信息,設置quiet參數,能夠不用打印這些信息。cold
:設置cold,會忽略執行importing和migrating。dots
:設置dots,則會在遷移過程打印遷移key數量的進度。update
:設置update,則會更新內存信息,方便之後的操做。move_slot流程以下:
cluster importing
命令,對目的節點執行migrating
命令。fix的時候有可能importing和migrating已經執行過來,因此此種場景會設置cold。cluster getkeysinslot
命令,一次性獲取遠節點遷移slot的pipeline個key的數量.cluster getkeysinslot
命令,直到返回的key數量爲0,就退出循環。cluster setslot
命令,把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命令流程以下:
--weight <arg>
,爲每一個設置的節點分配權重,沒有設置的節點,則權重默認爲1。--threshold <arg>
設置的閾值或者默認的閾值。計算的方式爲:先計算指望移動節點的閾值,算法爲:(100-(100.0*expected/n.slots.length)).abs,若是計算出的閾值沒有超出設置閾值,則不須要爲該節點移動slot。只要有一個master的移動節點超過閾值,就會觸發rebalance操做。六、用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流程影響不大。
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流程以下:
--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
返回的結果返回的節點順序。cluster replicate
命令複製master節點。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流程以下:
cluster forget
命令,從每一個節點中去除該節點。若是刪除的節點是master,並且它有slave的話,這些slave會去複製其餘master,調用的方法是get_master_with_least_replicas,與add-node沒設置--master-id
尋找master的方法同樣。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命令也是須要經過集羣的一個節點地址,連上整個集羣,而後在集羣的每一個節點執行該命令。
$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節點數據導入集羣。導入的流程以下:
--copy
參數,則傳遞copy參數,若是設置了--replace
,則傳遞replace參數。這中間若是出現異常,程序就會中止。沒使用--copy
模式,則能夠從新執行import命令,使用--copy
的話,最好清空新的集羣再導入一次。
import命令更適合離線的把外部redis數據導入,在線導入的話最好使用更專業的導入工具,以slave的方式鏈接redis節點去同步節點數據應該是更好的方式。