1.檢查GCC是否安裝,可以看看版本號 gcc -v 安裝命令:yum install gcc-c++ 2.安裝Ruby和Rubygems 如果有網的話,則通過yum命令進行安裝,自動將關聯的依賴包全部安裝 yum install ruby yum install rubygems Ruby離線安裝去官網下載:http://www.ruby-lang.org/en/downloads/ ./configure –-prefix=/usr/local/ruby -prefix是將ruby安裝到指定目錄,也可以自定義 make && make install 檢查版本 ruby -v 配置環境變量 which gem export RUBY_HOME=/usr/local/ruby export PATH=$PATH:RUBY_HOME/bin source /etc/profile 測試echo $PATH Rubygems離線安裝去官網下載 https://rubygems.org/pages/download。 解壓並進入目錄,執行命令:ruby setup.rb 更新 RubyGems 命令 gem update --system 配置環境變量 which gem export RUBY_GEMS = XXXX export PATH=$PATH:RUBY_HOME/bin:$RUBY_GEMS/bin source /etc/profile 測試echo $PATH 3.安裝對應的Redis的Rbuy接口包->Redis的gem包 官網下載https://rubygems.org/gems/redis/ 安裝 gem install redis-3.3.0.gem 如果報錯 ERROR: Loading command: install (LoadError) cannot load such file -- zlib ERROR: While executing gem ... (NoMethodError) undefined method `invoke_with_build_args' for nil:NilClass 我們需要再安裝zlib才行,下載zlib,上傳解壓,安裝zlib官方網站:http://www.zlib.net ,最新版1.2.11,安裝我們就一筆帶過 1 $tar -xvzf zlib-1.2.11.tar.gz 2 $cd zlib-1.2.8.tar.gz 3 $./configure --prefix=/usr/local/zlib 設置安裝路徑 4 $make 5 $make instal 又報錯 *** extconf.rb failed *** Could not create Makefile due to some reason, probably lack of necessary libraries and/or headers. Check the mkmf.log file for more details. You may need configuration options 1 $ cd /home/cmfchina/ruby/ruby-2.4.1/ext/zlib #備注:/home/cmfchina/ruby/ruby-2.4.1這個目錄是ruby安裝包后解壓的目錄,就是前面提到的ruby離線安裝 2 $ ruby extconf.rb --with-zlib-include=/usr/local/zlib/include/ --with-zlib-lib=/usr/local/zlib/lib #這時候會生成一個Makefile文件 備注:/usr/local/zlib是我的zlib安裝目錄 3 $ make && make install 接下來我們make && make install 安裝一下,但是當我們make的時候,又出現了錯誤如下 make: *** No rule to make target `/include/ruby.h', needed by `zlib.o'. Stop vim Makefile 修改如下信息 zlib.o: $(top_srcdir)/include/ruby.h 改成:zlib.o: ../../include/ruby.h 修改完成,然后保存:接着我們再make && make install 我們回到redis的gem目錄下,繼續執行命令:gem install redis-3.3.0.gem 又出現了錯誤,提示安裝我們還需要安裝OpenSSL,因為Redis集群交互是需要OpenSSL 我們又得安裝OpenSSL才行,官網地址:https://www.openssl.org/source/ 上次壓縮包到服務器,解壓, 1 $ tar -xzvf openssl-1.0.2l.tar.gz 2 $ cd openssl-1.0.2l 3 $ ./config -fPIC --prefix=/usr/local/openssl enable-shared 4 $ ./config -t 5 $ make && make install 到Ruby解壓的源碼[/home/cmfchina/ruby-2.4.1]目錄下的ext/openssl 目錄 1 $ cd /home/cmfchina/ruby-2.4.1/ext/openssl 2 備注:/home/cmfchina/ruby/ruby-2.4.1這個目錄是ruby安裝包后解壓的目錄,就是前面提到的ruby離線安裝 3 $ruby extconf.rb --with-openssl-include=/usr/local/openssl/include/ --with-openssl-lib=/usr/local/openssl/lib //會生成一個Makefile文件 4 備注:/usr/local/openssl是我的openssl安裝目錄 5 $ make && make install 如出錯make: *** No rule to make target `/include/ruby.h', needed by `ossl.o'. Stop 將$(top_srcdir)全部改成../.. 再回到之前redis目錄下執行命令:gem install redis-3.3.0.gem 4.安裝redis 到官網(https://redis.io/download)下載Redis 解壓進入目錄編譯安裝 # make && make install //make 這里如果不指定PREFIX,默認將安裝在/usr/local/bin下,保持默認就好 make && make install PREFIX=/oradata/redis 拷貝Redis/src 目錄下 redis-trib.rb 和Redis目錄下的redis.conf 文檔到 PREFIX的 bin 目錄下統一管理 創建及集群文件夾和6個節點 mkdir redis_cluster mkdir 700{1,2,3,4,5,6} 拷貝 redis.conf 到節點文件700*目錄下 開始集群配置 port 7000 //端口7000,7002,7003 bind 本機ip //默認ip為127.0.0.1 需要改為其他節點機器可訪問的ip 否則創建集群時無法訪問對應的端口,無法創建集群 daemonize yes //redis后台運行 pidfile /var/run/redis_7000.pid //pidfile文件對應7000,7001,7002 logfile /var/run/redis_7000.log //logfile日志文件 cluster-enabled yes //開啟集群 把注釋#去掉 cluster-config-file nodes_7000.conf //集群的配置 配置文件首次啟動自動生成 7000,7001,7002 cluster-node-timeout 15000 //請求超時 默認15秒,可自行設置 appendonly yes //aof日志開啟 有需要就開啟,它會每次寫操作都記錄一條日志 啟動bin目錄下執行./redis-service 700*/redis.conf 檢查啟動 ps -ef | grep redis netstat -tnlp | grep redis 考慮創建啟動腳本放在redis_cluster目錄下 5.啟動集群 ./redis-trib.rb create --replicas 1 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 127.0.0.1:7006 6.Redis集群測試 說明:-h+host –p+端口號 –c 是要連接集群, redis-cli -p 7001 -c redis-cli -c -h 127.0.0.1 -p 7001 shutdown //關閉集群,如果沒有-h參數,默認連接127.0.0.1,如果沒有-p參數,默認連接6370端口(所有如果用默認的,就沒有-h -p) cluster info :打印集群的信息 cluster nodes :列出集群當前已知的所有節點( node),以及這些節點的相關信息。 7.重啟集群 pkill -9 redis rm -rf dump.rdb rm -rf nodes-*.* 重啟redis節點,逐個啟動 創建集群 8.集群檢查 可以使用trib的check檢測的集群狀態 redis-trib.rb check xxx:7001 如果出現以上的錯誤,可以嘗試使用fix命令修復 redis-trib.rb fix xxx:7001 如果通過fix方式修復不了 pkill redis停止之前的redis進程 然后把aof,rdb,nodes節點文件刪除,刪除之前需要備份 ./redis-trib.rb check 10.166.15.35:7001 edis-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提供了哪些功能。 $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主要有兩個類:ClusterNode和RedisTrib。ClusterNode保存了每個節點的信息,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的執行結果: $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節點去同步節點數據應該是更好的方式。 下面是一個例子 ./redis-trib.rb import --from 10.0.10.1:6379 10.10.10.1:7000 上面的命令是把 10.0.10.1:6379(redis 2.8)上的數據導入到 10.10.10.1:7000這個節點所在的集群
