来源 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节点去同步节点数据应该是更好的方式。