jinfeng_wang

          G-G-S,D-D-U!

          BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
            400 Posts :: 0 Stories :: 296 Comments :: 0 Trackbacks
          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行左右的代碼,就實現(xiàn)了強大的集群操作。本文對redis-trib.rb的介紹是基于redis 3.0.6版本的源碼。閱讀本文需要對redis集群功能有一定的了解。關(guān)于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:創(chuàng)建集群
          • 2、check:檢查集群
          • 3、info:查看集群信息
          • 4、fix:修復集群
          • 5、reshard:在線遷移slot
          • 6、rebalance:平衡集群節(jié)點slot數(shù)量
          • 7、add-node:將新節(jié)點加入集群
          • 8、del-node:從集群中刪除節(jié)點
          • 9、set-timeout:設(shè)置集群節(jié)點間心跳連接的超時時間
          • 10、call:在集群全部節(jié)點上執(zhí)行命令
          • 11、import:將外部redis數(shù)據(jù)導入集群

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

          redis-trib.rb主要有兩個類:ClusterNodeRedisTribClusterNode保存了每個節(jié)點的信息,RedisTrib則是redis-trib.rb各個功能的實現(xiàn)。

          ClusterNode對象

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

          • @r:執(zhí)行redis命令的客戶端對象。
          • @info:保存了該節(jié)點的詳細信息,包括cluster nodes命令中自己這行的信息和cluster info的信息。
          • @dirty:節(jié)點信息是否需要更新,如果為true,我們需要把內(nèi)存的節(jié)點更新信息到節(jié)點上。
          • @friends:保存了集群其他節(jié)點的info信息。其信息為通過cluster nodes命令獲得的其他節(jié)點信息。

          ClusterNode有下面一些成員方法:

          • initialize:ClusterNode的構(gòu)造方法,需要傳入節(jié)點的地址信息。
          • friends:返回@friends對象。
          • slots:返回該節(jié)點負責的slots信息。
          • has_flag?:判斷節(jié)點info信息的的flags中是否有給定的flag。
          • to_s:類似java的toString方法,返回節(jié)點的地址信息。
          • connect:連接redis節(jié)點。
          • assert_cluster:判斷節(jié)點開啟了集群配置。
          • assert_empty:確定節(jié)點目前沒有跟任何其他節(jié)點握手,同時自己的db數(shù)據(jù)為空。
          • load_info:通過cluster info和cluster nodes導入節(jié)點信息。
          • add_slots:給節(jié)點增加slot,該操作只是在內(nèi)存中修改,并把dirty設(shè)置成true,等待flush_node_config將內(nèi)存中的數(shù)據(jù)同步在節(jié)點執(zhí)行。
          • set_as_replica:slave設(shè)置復制的master地址。dirty設(shè)置成true。
          • flush_node_config:將內(nèi)存的數(shù)據(jù)修改同步在集群節(jié)點中執(zhí)行。
          • info_string:簡單的info信息。
          • get_config_signature:用來驗證集群節(jié)點見的cluster nodes信息是否一致。該方法返回節(jié)點的簽名信息。
          • info:返回@info對象,包含詳細的info信息。
          • is_dirty?:判斷@dirty。
          • r:返回執(zhí)行redis命令的客戶端對象。

          有了ClusterNode對象,在處理集群操作的時候,就獲得了集群的信息,可以進行集群相關(guān)操作。在此先簡單介紹下redis-trib.rb腳本的使用,以create為例:

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

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

          其他命令大都需要傳遞host:port,這是redis-trib.rb為了連接集群,需要選擇集群中的一個節(jié)點,然后通過該節(jié)點獲得整個集群的信息。

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

          create創(chuàng)建集群

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

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

          有一個slave的創(chuàng)建命令如下:

          $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  

          創(chuàng)建流程如下:

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

          check檢查集群

          檢查集群狀態(tài)的命令,沒有其他參數(shù),只需要選擇一個集群中的一個節(jié)點即可。執(zhí)行命令以及結(jié)果如下:

          $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.     

          檢查前會先執(zhí)行l(wèi)oad_cluster_info_from_node方法,把所有節(jié)點數(shù)據(jù)load進來。load的方式為通過自己的cluster nodes發(fā)現(xiàn)其他節(jié)點,然后連接每個節(jié)點,并加入nodes數(shù)組。接著生成節(jié)點間的復制關(guān)系。

          load完數(shù)據(jù)后,開始檢查數(shù)據(jù),檢查的方式也是調(diào)用創(chuàng)建時候使用的check_cluster。

          info查看集群信息

          info命令用來查看集群的信息。info命令也是先執(zhí)行l(wèi)oad_cluster_info_from_node獲取完整的集群信息。然后顯示ClusterNode的info_string結(jié)果,示例如下:

          $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方法內(nèi)傳入fix為
          true的變量,會在集群檢查出現(xiàn)異常的時候執(zhí)行修復流程。目前fix命令能修復兩種異常,一種是集群有處于遷移中的slot的節(jié)點,一種是slot未完全分配的異常。

          fix_open_slot方法是修復集群有處于遷移中的slot的節(jié)點異常。

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

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

          • 1、所有節(jié)點的該slot都沒有數(shù)據(jù)。該狀態(tài)redis-trib.rb工具直接采用隨機分配的方式,并沒有考慮節(jié)點的均衡。本人嘗試對沒有分配slot的集群通過fix修復集群,結(jié)果slot還是能比較平均的分配,但是沒有了連續(xù)性,打印的slot信息非常離散。
          • 2、有一個節(jié)點的該slot有數(shù)據(jù)。該狀態(tài)下,直接把slot分配給該slot有數(shù)據(jù)的節(jié)點。
          • 3、有多個節(jié)點的該slot有數(shù)據(jù)。此種情況目前還處于TODO狀態(tài),不過redis作者列出了修復的步驟,對這些節(jié)點,除第一個節(jié)點,執(zhí)行cluster migrating命令,然后把這些節(jié)點的數(shù)據(jù)遷移到第一個節(jié)點上。清除migrating狀態(tài),然后把slot分配給第一個節(jié)點。

          reshard在線遷移slot

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

          reshard的參數(shù)很多,下面來一一解釋一番:

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

          遷移的流程如下:

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

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

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

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

          下面看下一次reshard的執(zhí)行結(jié)果:

          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的全部數(shù)據(jù)從源節(jié)點遷移到目的節(jié)點,fix、reshard、rebalance都需要調(diào)用該方法遷移slot。

          move_slot接受下面幾個參數(shù),

          • 1、pipeline:設(shè)置一次從slot上獲取多少個key。
          • 2、quiet:遷移會打印相關(guān)信息,設(shè)置quiet參數(shù),可以不用打印這些信息。
          • 3、cold:設(shè)置cold,會忽略執(zhí)行importing和migrating。
          • 4、dots:設(shè)置dots,則會在遷移過程打印遷移key數(shù)量的進度。
          • 5、update:設(shè)置update,則會更新內(nèi)存信息,方便以后的操作。

          move_slot流程如下:

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

          rebalance平衡集群節(jié)點slot數(shù)量

          rebalance命令可以根據(jù)用戶傳入的參數(shù)平衡集群節(jié)點的slot數(shù)量,rebalance功能非常強大,可以傳入的參數(shù)很多,以下是rebalance的參數(shù)列表和命令示例。

          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 

          下面也先一一解釋下每個參數(shù)的用法:

          • host:port:這個是必傳參數(shù),用來從一個節(jié)點獲取整個集群信息,相當于獲取集群信息的入口。
          • --weight <arg>:節(jié)點的權(quán)重,格式為node_id=weight,如果需要為多個節(jié)點分配權(quán)重的話,需要添加多個--weight <arg>參數(shù),即--weight b31e3a2e=5 --weight 60b8e3a1=5,node_id可為節(jié)點名稱的前綴,只要保證前綴位數(shù)能唯一區(qū)分該節(jié)點即可。沒有傳遞–weight的節(jié)點的權(quán)重默認為1。
          • --auto-weights:這個參數(shù)在rebalance流程中并未用到。
          • --threshold <arg>:只有節(jié)點需要遷移的slot閾值超過threshold,才會執(zhí)行rebalance操作。具體計算方法可以參考下面的rebalance命令流程的第四步。
          • --use-empty-masters:rebalance是否考慮沒有節(jié)點的master,默認沒有分配slot節(jié)點的master是不參與rebalance的,設(shè)置--use-empty-masters可以讓沒有分配slot的節(jié)點參與rebalance。
          • --timeout <arg>:設(shè)置migrate命令的超時時間。
          • --simulate:設(shè)置該參數(shù),可以模擬rebalance操作,提示用戶會遷移哪些slots,而不會真正執(zhí)行遷移操作。
          • --pipeline <arg>:與reshar的pipeline參數(shù)一樣,定義cluster getkeysinslot命令一次取出的key數(shù)量,不傳的話使用默認值為10。

          rebalance命令流程如下:

          • 1、load_cluster_info_from_node方法先加載集群信息。
          • 2、計算每個master的權(quán)重,根據(jù)參數(shù)--weight <arg>,為每個設(shè)置的節(jié)點分配權(quán)重,沒有設(shè)置的節(jié)點,則權(quán)重默認為1。
          • 3、根據(jù)每個master的權(quán)重,以及總的權(quán)重,計算自己期望被分配多少個slot。計算的方式為:總slot數(shù)量 * (自己的權(quán)重 / 總權(quán)重)。
          • 4、計算每個master期望分配的slot是否超過設(shè)置的閾值,即--threshold <arg>設(shè)置的閾值或者默認的閾值。計算的方式為:先計算期望移動節(jié)點的閾值,算法為:(100-(100.0*expected/n.slots.length)).abs,如果計算出的閾值沒有超出設(shè)置閾值,則不需要為該節(jié)點移動slot。只要有一個master的移動節(jié)點超過閾值,就會觸發(fā)rebalance操作。
          • 5、如果觸發(fā)了rebalance操作。那么就開始執(zhí)行rebalance操作,先將每個節(jié)點當前分配的slots數(shù)量減去期望分配的slot數(shù)量獲得balance值。將每個節(jié)點的balance從小到大進行排序獲得sn數(shù)組。
          • 6、用dst_idx和src_idx游標分別從sn數(shù)組的頭部和尾部開始遍歷。目的是為了把尾部節(jié)點的slot分配給頭部節(jié)點。

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

          • 7、獲取sn[dst_idx]和sn[src_idx]的balance值較小的那個值,該值即為需要從sn[src_idx]節(jié)點遷移到sn[dst_idx]節(jié)點的slot數(shù)量。
          • 8、接著通過compute_reshard_table方法計算源節(jié)點的slot如何分配到源節(jié)點列表。這個方法在reshard流程中也有調(diào)用,具體步驟可以參考reshard流程的第六步。
          • 9、如果是simulate模式,則只是打印出遷移列表。
          • 10、如果沒有設(shè)置simulate,則執(zhí)行move_slot操作,遷移slot,傳入的參數(shù)為: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將新節(jié)點加入集群

          add-node命令可以將新節(jié)點加入集群,節(jié)點可以為master,也可以為某個master節(jié)點的slave。

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

          add-node有兩個可選參數(shù):

          • --slave:設(shè)置該參數(shù),則新節(jié)點以slave的角色加入集群
          • --master-id:這個參數(shù)需要設(shè)置了--slave才能生效,--master-id用來指定新節(jié)點的master節(jié)點。如果不設(shè)置該參數(shù),則會隨機為節(jié)點選擇master節(jié)點。

          可以看下add-node命令的執(zhí)行示例:

          $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方法轉(zhuǎn)載集群信息,check_cluster方法檢查集群是否健康。
          • 2、如果設(shè)置了--slave,則需要為該節(jié)點尋找master節(jié)點。設(shè)置了--master-id,則以該節(jié)點作為新節(jié)點的master,如果沒有設(shè)置--master-id,則調(diào)用get_master_with_least_replicas方法,尋找slave數(shù)量最少的master節(jié)點。如果slave數(shù)量一致,則選取load_cluster_info_from_node順序發(fā)現(xiàn)的第一個節(jié)點。load_cluster_info_from_node順序的第一個節(jié)點是add-node設(shè)置的existing_host:existing_port節(jié)點,后面的順序根據(jù)在該節(jié)點執(zhí)行cluster nodes返回的結(jié)果返回的節(jié)點順序。
          • 3、連接新的節(jié)點并與集群第一個節(jié)點握手。
          • 4、如果沒設(shè)置–slave就直接返回ok,設(shè)置了–slave,則需要等待確認新節(jié)點加入集群,然后執(zhí)行cluster replicate命令復制master節(jié)點。
          • 5、至此,完成了全部的增加節(jié)點的流程。

          del-node從集群中刪除節(jié)點

          del-node可以把某個節(jié)點從集群中刪除。del-node只能刪除沒有分配slot的節(jié)點。刪除命令傳遞兩個參數(shù):

          • host:port:從該節(jié)點獲取集群信息。
          • node_id:需要刪除的節(jié)點id。

          del-node執(zhí)行結(jié)果示例如下:

          $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方法轉(zhuǎn)載集群信息。
          • 2、根據(jù)傳入的node id獲取節(jié)點,如果節(jié)點沒找到,則直接提示錯誤并退出。
          • 3、如果節(jié)點分配的slot不為空,則直接提示錯誤并退出。
          • 4、遍歷集群內(nèi)的其他節(jié)點,執(zhí)行cluster forget命令,從每個節(jié)點中去除該節(jié)點。如果刪除的節(jié)點是master,而且它有slave的話,這些slave會去復制其他master,調(diào)用的方法是get_master_with_least_replicas,與add-node沒設(shè)置--master-id尋找master的方法一樣。
          • 5、然后關(guān)閉該節(jié)點。

          set-timeout設(shè)置集群節(jié)點間心跳連接的超時時間

          set-timeout用來設(shè)置集群節(jié)點間心跳連接的超時時間,單位是毫秒,不得小于100毫秒,因為100毫秒對于心跳時間來說太短了。該命令修改是節(jié)點配置參數(shù)cluster-node-timeout,默認是15000毫秒。通過該命令,可以給每個節(jié)點設(shè)置超時時間,設(shè)置的方式使用config set命令動態(tài)設(shè)置,然后執(zhí)行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在集群全部節(jié)點上執(zhí)行命令

          call命令可以用來在集群的全部節(jié)點執(zhí)行相同的命令。call命令也是需要通過集群的一個節(jié)點地址,連上整個集群,然后在集群的每個節(jié)點執(zhí)行該命令。

          $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數(shù)據(jù)導入集群

          import命令可以把外部的redis節(jié)點數(shù)據(jù)導入集群。導入的流程如下:

          • 1、通過load_cluster_info_from_node方法轉(zhuǎn)載集群信息,check_cluster方法檢查集群是否健康。
          • 2、連接外部redis節(jié)點,如果外部節(jié)點開啟了cluster_enabled,則提示錯誤。
          • 3、通過scan命令遍歷外部節(jié)點,一次獲取1000條數(shù)據(jù)。
          • 4、遍歷這些key,計算出key對應的slot。
          • 5、執(zhí)行migrate命令,源節(jié)點是外部節(jié)點,目的節(jié)點是集群slot對應的節(jié)點,如果設(shè)置了--copy參數(shù),則傳遞copy參數(shù),如果設(shè)置了--replace,則傳遞replace參數(shù)。
          • 6、不停執(zhí)行scan命令,直到遍歷完全部的key。
          • 7、至此完成整個遷移流程

          這中間如果出現(xiàn)異常,程序就會停止。沒使用--copy模式,則可以重新執(zhí)行import命令,使用--copy的話,最好清空新的集群再導入一次。

          import命令更適合離線的把外部redis數(shù)據(jù)導入,在線導入的話最好使用更專業(yè)的導入工具,以slave的方式連接redis節(jié)點去同步節(jié)點數(shù)據(jù)應該是更好的方式。

          posted on 2016-12-14 18:32 jinfeng_wang 閱讀(454) 評論(0)  編輯  收藏 所屬分類: 2016-REDIS
          主站蜘蛛池模板: 阿荣旗| 嘉黎县| 东乡族自治县| 南阳市| 孝昌县| 新平| 平泉县| 连山| 云和县| 石景山区| 绵竹市| 富源县| 宜兰市| 芒康县| 定西市| 禄丰县| 佛坪县| 宁晋县| 西昌市| 三台县| 红安县| 扶余县| 清原| 淄博市| 西乌珠穆沁旗| 虞城县| 凤山市| 沐川县| 介休市| 资源县| 临猗县| 民县| 甘孜县| 高雄县| 洛宁县| 廉江市| 永登县| 南华县| 高清| 台北县| 长治市|