前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

sentinel是高可用,那么集群的话就是分布式方案了。redis分布方案有两种

客户端分区方案,优点是分区逻辑可控,缺点是需要自己处理数据路由,高可用,故障转移等问题

代理方案:优点是简化客户端分布式逻辑,缺点是加重架构部署复杂度和性能消耗(我们公司就是使用的代理负载均衡)

1、数据分布(Redis cluster采用哈希分区规则)

1、节点取余分区

使用特定的数据,如redis的key,再更加节点的数量N使用公式 hash(key)%N,计算出hash值,用来决定数据映射到哪个节点上(和hashmap有点像)当节点数量变化的时候,如收缩或者扩容,数据节点的映射关系需要重新计算。

2、一致性哈希分区

为每个节点分配一个token,范围一般在0-2的32次幂,这些token构成一个哈希换,数据进行读写操作的时候,先根据key计算hash值,然后顺时针找到第一个大于等于该哈希值的toaken节点。

好处:假如和删除节点只影响哈希环中相邻的节点。对其他节点没有影响。 缺点:加减节点会造成hash环中的部分数据无法命中。需要手动处理或者忽略掉这部分数据。正因为存在这些缺点所以一般采用虚拟槽进行改进

3、虚拟槽分区

当前集群有5个节点,每个及诶单平均复制3276个槽,由于采用高质量的hash算法,每个槽的数据比较均匀,将数据平均分到5个节点进行分区,Redis Cluster就是采用这种分区

2、集群功能限制

1、key的事物操作支持有限,只支持多个key在同一节点的事物操作,当多个key分布在不同的节点上的时候无法使用事物功能

2、不支持多数据库空间,redis,也就是值能使用一个数据库空间 db等于0

3、复制结构只支持一层,不支持嵌套树状结构。从节点只能复制主节点

4、批量操作支持有限 mset ,mget 目前只支持具有相同slot值的批量操作,因为mset 其实也就是多个key的存储,可能存储到不同的节点也说不定。所以不支持

3、搭建集群(使用redis-trib.rb工具)

准备工作: 1、Redis集群节点数量至少为6个才能保证组完成高可用的集群 2、集群节点统一目录,一般为3个目录,conf,data,log分布存放配置,数据和日志相关配置文件

daemonize yes 
logfile "6379.log"  //也就是说redis-server启动之后的日志放到了dir目录下的6379.log
dbfilename "dump-6379.rdb"
dir "/usr/local/redis/dir"


cluster-enabled yes   //开启集群模式
cluster-config-file nodes-6481.conf   //集群内部配置文件
cluster-node-timeout 15000  //节点超时时间 


建议使用linux系统操作

1、Ruby环境准备

安装完成之后,看看环境是否正确,重新安装一个redis端口为6379,就用这个redis来操作搭建集群了。

healerjean$ ./redis-trib.rb
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>

2、开始创建集群

1、–replicas 参数指定集群中没给主节点配备几个从节点,这里设置为1,就是说每个主节点分配1个从节点,一般是先主节点,前三个主节点,再从节点,后三个从节点。可以通过下面的命令日志可以观察到

Using 3 masters:
127.0.0.1:6481
127.0.0.1:6482
127.0.0.1:6483
Adding replica 127.0.0.1:6484 to 127.0.0.1:6481
Adding replica 127.0.0.1:6485 to 127.0.0.1:6482
Adding replica 127.0.0.1:6486 to 127.0.0.1:6483

2、如果部署节点尽量使用不同的IP地址。redis-trib.rb会尽可能保证主从节点不在同一个机器上。因此会重新排列节点的列表的顺序

3、下面应该使用redis服务器所在的Ip,我这里是本机测试使用,所以使用的127.0.0.1,如果是windows加虚拟机就会出问题。尽管连接了虚拟机的IP,但是代码连接不到redis。所以虚拟机也要使用虚拟机所在ip

./redis-trib.rb  create --replicas 1 127.0.0.1:6481 127.0.0.1:6482 127.0.0.1:6483 127.0.0.1:6484 127.0.0.1:6485 127.0.0.1:6486

2.1、开始执行命令

healerjean$ ./redis-trib.rb  create --replicas 1 127.0.0.1:6481 127.0.0.1:6482 127.0.0.1:6483 127.0.0.1:6484 127.0.0.1:6485 127.0.0.1:6486
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
127.0.0.1:6481
127.0.0.1:6482
127.0.0.1:6483
Adding replica 127.0.0.1:6484 to 127.0.0.1:6481
Adding replica 127.0.0.1:6485 to 127.0.0.1:6482
Adding replica 127.0.0.1:6486 to 127.0.0.1:6483
M: 518f740fa4823ad65972f0605983be347f9ccc79 127.0.0.1:6481
   slots:0-5460 (5461 slots) master
M: 1ed5a33205eac6bcd02672731b8fff55485bd81b 127.0.0.1:6482
   slots:5461-10922 (5462 slots) master
M: 4b420c1a34f8d1053e314c05776fbfc50a3cba5e 127.0.0.1:6483
   slots:10923-16383 (5461 slots) master
S: 96b105f0e33e3c5ccbe5983c7f65f52e7971eac9 127.0.0.1:6484
   replicates 518f740fa4823ad65972f0605983be347f9ccc79
S: 9c8806f52d7e71c0ef2dc7908879dc84fe40e5c8 127.0.0.1:6485
   replicates 1ed5a33205eac6bcd02672731b8fff55485bd81b
S: 42e99055fef2fc54030b1764878925ba878551a8 127.0.0.1:6486
   replicates 4b420c1a34f8d1053e314c05776fbfc50a3cba5e
Can I set the above configuration? (type 'yes' to accept): 

2.1、执行节点握手和槽的fenpei

可以看到下面的16384个槽全部被分配完毕,而且分配均匀给了master节点

输入 yes

Can I set the above configuration? (type 'yes' to accept): yes
>>> Nodes configuration updated
>>> Assign a different config epoch to each node
>>> Sending CLUSTER MEET messages to join the cluster
Waiting for the cluster to join...
>>> Performing Cluster Check (using node 127.0.0.1:6481)
M: 518f740fa4823ad65972f0605983be347f9ccc79 127.0.0.1:6481
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: 4b420c1a34f8d1053e314c05776fbfc50a3cba5e 127.0.0.1:6483
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 42e99055fef2fc54030b1764878925ba878551a8 127.0.0.1:6486
   slots: (0 slots) slave
   replicates 4b420c1a34f8d1053e314c05776fbfc50a3cba5e
S: 9c8806f52d7e71c0ef2dc7908879dc84fe40e5c8 127.0.0.1:6485
   slots: (0 slots) slave
   replicates 1ed5a33205eac6bcd02672731b8fff55485bd81b
S: 96b105f0e33e3c5ccbe5983c7f65f52e7971eac9 127.0.0.1:6484
   slots: (0 slots) slave
   replicates 518f740fa4823ad65972f0605983be347f9ccc79
M: 1ed5a33205eac6bcd02672731b8fff55485bd81b 127.0.0.1:6482
   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.
MacBook-Pro:src healerjean$ 

3、集群完整性检查和master和slave信息查看

集群完整性是指所有的槽都分配到了存活的主节点上,只要有一个槽没有分配给主主节点,则表示不完整。使用下面的命令检测,只要给出集群中任意一个节点就也可以看看集群是否完成。6379则不能成功,因为它不属于集群

./redis-trib.rb check 127.0.0.1:6481

出现结果是:[OK] All 16384 slots covered.则表示集群创建完整


MacBook-Pro:src healerjean$ ./redis-trib.rb check 127.0.0.1:6379
[ERR] Node 127.0.0.1:6379 is not configured as a cluster node.
MacBook-Pro:src healerjean$ ./redis-trib.rb check 127.0.0.1:6481
>>> Performing Cluster Check (using node 127.0.0.1:6481)
M: 518f740fa4823ad65972f0605983be347f9ccc79 127.0.0.1:6481
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: 4b420c1a34f8d1053e314c05776fbfc50a3cba5e 127.0.0.1:6483
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 42e99055fef2fc54030b1764878925ba878551a8 127.0.0.1:6486
   slots: (0 slots) slave
   replicates 4b420c1a34f8d1053e314c05776fbfc50a3cba5e
S: 9c8806f52d7e71c0ef2dc7908879dc84fe40e5c8 127.0.0.1:6485
   slots: (0 slots) slave
   replicates 1ed5a33205eac6bcd02672731b8fff55485bd81b
S: 96b105f0e33e3c5ccbe5983c7f65f52e7971eac9 127.0.0.1:6484
   slots: (0 slots) slave
   replicates 518f740fa4823ad65972f0605983be347f9ccc79
M: 1ed5a33205eac6bcd02672731b8fff55485bd81b 127.0.0.1:6482
   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.
MacBook-Pro:src healerjean$ 

4、集群扩容

准备连个节点,一个将来是主节点,一个是从节点 这里为了和上面的的好区分,节点的端口设置为6385和6386

redis为我们提供了一种假如集群的命令但是不建议使用,而是使用ruby的命令,redis提供的命令为如下,当下面的节点已经假如其他集群,下面的命令会将它所在的集群和本集群进行合并。而使用redis-trib.rb则不会,因为他会检查,同时放弃加入集群的操作

不建议使用
cluster meet 127.0.0.1 6385 
建议使用:第一个参数为新节点的“”ip:端口“”,第二个参数为集群中的任一有效的节点,也就是随便写

./redis-trib.rb add-node 127.0.0.1:6385 127.0.0.1:6481 

4.1、开始扩容加入master主节点

观察到 [OK] New node added correctly. 表示成功

healerjean$ ./redis-trib.rb add-node 127.0.0.1:6385 127.0.0.1:6481 
>>> Adding node 127.0.0.1:6385 to cluster 127.0.0.1:6481
>>> Performing Cluster Check (using node 127.0.0.1:6481)
M: 518f740fa4823ad65972f0605983be347f9ccc79 127.0.0.1:6481
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: 4b420c1a34f8d1053e314c05776fbfc50a3cba5e 127.0.0.1:6483
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 42e99055fef2fc54030b1764878925ba878551a8 127.0.0.1:6486
   slots: (0 slots) slave
   replicates 4b420c1a34f8d1053e314c05776fbfc50a3cba5e
S: 9c8806f52d7e71c0ef2dc7908879dc84fe40e5c8 127.0.0.1:6485
   slots: (0 slots) slave
   replicates 1ed5a33205eac6bcd02672731b8fff55485bd81b
S: 96b105f0e33e3c5ccbe5983c7f65f52e7971eac9 127.0.0.1:6484
   slots: (0 slots) slave
   replicates 518f740fa4823ad65972f0605983be347f9ccc79
M: 1ed5a33205eac6bcd02672731b8fff55485bd81b 127.0.0.1:6482
   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.
>>> Send CLUSTER MEET to node 127.0.0.1:6385 to make it join the cluster.
[OK] New node added correctly.
MacBook-Pro:src healerjean$ 

4.2、检查master节点的槽

随便进入一个节点,这里我选了主节点6481,输入下面的命令之后,我们能看到并没有给它分配槽。这个槽后面我们慢慢分配

cluster nodes,当然也可以使用rb的check命令
127.0.0.1:6481> cluster nodes
5041f91a03330df4c0667b91f11151df965cdaad 127.0.0.1:6385 master - 0 1524762381923 0 connected
4b420c1a34f8d1053e314c05776fbfc50a3cba5e 127.0.0.1:6483 master - 0 1524762386469 3 connected 10923-16383
518f740fa4823ad65972f0605983be347f9ccc79 127.0.0.1:6481 myself,master - 0 0 1 connected 0-5460
42e99055fef2fc54030b1764878925ba878551a8 127.0.0.1:6486 slave 4b420c1a34f8d1053e314c05776fbfc50a3cba5e 0 1524762384953 6 connected
9c8806f52d7e71c0ef2dc7908879dc84fe40e5c8 127.0.0.1:6485 slave 1ed5a33205eac6bcd02672731b8fff55485bd81b 0 1524762388993 5 connected
96b105f0e33e3c5ccbe5983c7f65f52e7971eac9 127.0.0.1:6484 slave 518f740fa4823ad65972f0605983be347f9ccc79 0 1524762386975 4 connected
1ed5a33205eac6bcd02672731b8fff55485bd81b 127.0.0.1:6482 master - 0 1524762387983 2 connected 5461-10922
127.0.0.1:6481> 

4.3、给这个节点分配从节点

把6386当成slave加入. master-id 为自己作为slave时候,master的id,这里应该是6385主节点的id,可以通过check查看基本信息的id (后面的端口6481从集群中随便写)

./redis-trib.rb add-node --slave --master-id  5041f91a03330df4c0667b91f11151df965cdaad 127.0.0.1:6386 127.0.0.1:6481  

healerjean$ ./redis-trib.rb add-node --slave --master-id  5041f91a03330df4c0667b91f11151df965cdaad 127.0.0.1:6386 127.0.0.1:6481  
>>> Adding node 127.0.0.1:6386 to cluster 127.0.0.1:6481
>>> Performing Cluster Check (using node 127.0.0.1:6481)
M: 518f740fa4823ad65972f0605983be347f9ccc79 127.0.0.1:6481
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
M: 5041f91a03330df4c0667b91f11151df965cdaad 127.0.0.1:6385
   slots: (0 slots) master
   0 additional replica(s)
M: 4b420c1a34f8d1053e314c05776fbfc50a3cba5e 127.0.0.1:6483
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 42e99055fef2fc54030b1764878925ba878551a8 127.0.0.1:6486
   slots: (0 slots) slave
   replicates 4b420c1a34f8d1053e314c05776fbfc50a3cba5e
S: 9c8806f52d7e71c0ef2dc7908879dc84fe40e5c8 127.0.0.1:6485
   slots: (0 slots) slave
   replicates 1ed5a33205eac6bcd02672731b8fff55485bd81b
S: 96b105f0e33e3c5ccbe5983c7f65f52e7971eac9 127.0.0.1:6484
   slots: (0 slots) slave
   replicates 518f740fa4823ad65972f0605983be347f9ccc79
M: 1ed5a33205eac6bcd02672731b8fff55485bd81b 127.0.0.1:6482
   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.
>>> Send CLUSTER MEET to node 127.0.0.1:6386 to make it join the cluster.
Waiting for the cluster to join.
>>> Configure node as replica of 127.0.0.1:6385.
[OK] New node added correctly.
MacBook-Pro:src healerjean$ 

4.3.1、查看master和salve信息,可以观察到新加入的已经存在,但是却没有分配槽

./redis-trib check 127.0.0.1 6481

M: 5041f91a03330df4c0667b91f11151df965cdaad 127.0.0.1:6385
   slots: (0 slots) master
可以看到 从节点6386复制的是6385主节点
S: e4b8e373e245e7297a612b56126fd0e01afc7965 127.0.0.1:6386
   slots: (0 slots) slave
   replicates 5041f91a03330df4c0667b91f11151df965cdaad
M: 5041f91a03330df4c0667b91f11151df965cdaad 127.0.0.1:6385


healerjean$ ./redis-trib.rb check 127.0.0.1:6481
>>> Performing Cluster Check (using node 127.0.0.1:6481)
M: 518f740fa4823ad65972f0605983be347f9ccc79 127.0.0.1:6481
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
S: e4b8e373e245e7297a612b56126fd0e01afc7965 127.0.0.1:6386
   slots: (0 slots) slave
   replicates 5041f91a03330df4c0667b91f11151df965cdaad
M: 5041f91a03330df4c0667b91f11151df965cdaad 127.0.0.1:6385
   slots: (0 slots) master
   1 additional replica(s)
M: 4b420c1a34f8d1053e314c05776fbfc50a3cba5e 127.0.0.1:6483
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
S: 42e99055fef2fc54030b1764878925ba878551a8 127.0.0.1:6486
   slots: (0 slots) slave
   replicates 4b420c1a34f8d1053e314c05776fbfc50a3cba5e
S: 9c8806f52d7e71c0ef2dc7908879dc84fe40e5c8 127.0.0.1:6485
   slots: (0 slots) slave
   replicates 1ed5a33205eac6bcd02672731b8fff55485bd81b
S: 96b105f0e33e3c5ccbe5983c7f65f52e7971eac9 127.0.0.1:6484
   slots: (0 slots) slave
   replicates 518f740fa4823ad65972f0605983be347f9ccc79
M: 1ed5a33205eac6bcd02672731b8fff55485bd81b 127.0.0.1:6482
   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.
MacBook-Pro:src healerjean$ 

4.4、迁移槽和数据

槽在迁移的时候,可以正常提供读写服务,迁移过程是集群扩容最核心的环节。

4.4.1、槽迁移计划

首先需要为新及诶单制定槽的迁移计划,确保加入新节点之后每个主节点都负责相等数量的槽。从而保证数据均衡。 这个需要我们手动计算16384 /4 和 16383/3

4.4.2、迁移数据

数据迁移的是逐个槽进行的 迁移之前我们应该对槽和每个节点的key进行一个简单的查看,这只能看到主节点的信息;

 healerjean$ ./redis-trib.rb info 127.0.0.1:6481
MacBook-Pro:src healerjean$ ./redis-trib.rb info 127.0.0.1:6481
127.0.0.1:6481 (518f740f...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:6385 (5041f91a...) -> 0 keys | 0 slots | 1 slaves.
127.0.0.1:6483 (4b420c1a...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:6482 (1ed5a332...) -> 0 keys | 5462 slots | 1 slaves.
[OK] 0 keys in 4 masters.
0.00 keys per slot on average.
MacBook-Pro:src healerjean$ 

1、开始分区,下面的和端口随便在集群中找即可,这里还是使用6481,因为这个命令之后要求我们输入节点的运行id。然后 输入槽的数量16384/4=4096

./redis-trib.rb reshard 127.0.0.1:6481

[OK] All 16384 slots covered.
How many slots do you want to move (from 1 to 16384)? 4096//1、输入要分配槽的数量
What is the receiving node ID? 5041f91a03330df4c0667b91f11151df965cdaad//2、输入给哪个节点要分配槽,这里我先选择了6485

Please enter all the source node IDs.
  Type 'all' to use all the nodes as source nodes for the hash slots.
  Type 'done' once you entered all the source nodes IDs.
Source node #1:all //3、表示从所有的节点中取出数据分配给上面的id节点,之前我有点错误,所以下面只能看到正确的6385的槽的数量4096

Do you want to proceed with the proposed reshard plan (yes/no)? yes //4、yes表示分配完成



2、观察6385主节点的槽发现为4096,哈哈

MacBook-Pro:src healerjean$ ./redis-trib.rb info 127.0.0.1:6481
127.0.0.1:6481 (518f740f...) -> 0 keys | 10239 slots | 1 slaves.
127.0.0.1:6385 (5041f91a...) -> 0 keys | 4096 slots | 1 slaves.
127.0.0.1:6483 (4b420c1a...) -> 0 keys | 1024 slots | 1 slaves.
127.0.0.1:6482 (1ed5a332...) -> 0 keys | 1025 slots | 1 slaves.
[OK] 0 keys in 4 masters.
0.00 keys per slot on average.
MacBook-Pro:src healerjean$ 

3、给剩下的槽开始分配,其实通过上面选择要分配的节点的时候选择了all,我们可以知道Source node :是要求我们输入源节点分出我们上面输入4096+目标节点的数量 。

所以我们需要计算 比如,我瞎分之后如下,现在我们可以看到主节点6483最少是1024,它需要4096-1024=3072个节点,而6385已结可以了,所以不需要动6385节点,只要从6481或者拾6482中取出3072给它就可以了

MacBook-Pro:src healerjean$ ./redis-trib.rb info 127.0.0.1:6481
127.0.0.1:6481 (518f740f...) -> 0 keys | 6143 slots | 1 slaves.
127.0.0.1:6385 (5041f91a...) -> 0 keys | 4096 slots | 1 slaves.
127.0.0.1:6483 (4b420c1a...) -> 0 keys | 1024 slots | 1 slaves.
127.0.0.1:6482 (1ed5a332...) -> 0 keys | 5121 slots | 1 slaves.
[OK] 0 keys in 4 masters.
0.00 keys per slot on average.
MacBook-Pro:src healerjean$ 
MacBook-Pro:src healerjean$ ./redis-trib.rb reshard 127.0.0.1:6481
>>> Performing Cluster Check (using node 127.0.0.1:6481)
M: 518f740fa4823ad65972f0605983be347f9ccc79 127.0.0.1:6481
   slots:7510-9556,10923-15018 (6143 slots) master
   1 additional replica(s)
S: e4b8e373e245e7297a612b56126fd0e01afc7965 127.0.0.1:6386
   slots: (0 slots) slave
   replicates 5041f91a03330df4c0667b91f11151df965cdaad
M: 5041f91a03330df4c0667b91f11151df965cdaad 127.0.0.1:6385
   slots:0-3413,9557-9897,15019-15359 (4096 slots) master
   1 additional replica(s)
M: 4b420c1a34f8d1053e314c05776fbfc50a3cba5e 127.0.0.1:6483
   slots:15360-16383 (1024 slots) master
   1 additional replica(s)
S: 42e99055fef2fc54030b1764878925ba878551a8 127.0.0.1:6486
   slots: (0 slots) slave
   replicates 4b420c1a34f8d1053e314c05776fbfc50a3cba5e
S: 9c8806f52d7e71c0ef2dc7908879dc84fe40e5c8 127.0.0.1:6485
   slots: (0 slots) slave
   replicates 1ed5a33205eac6bcd02672731b8fff55485bd81b
S: 96b105f0e33e3c5ccbe5983c7f65f52e7971eac9 127.0.0.1:6484
   slots: (0 slots) slave
   replicates 518f740fa4823ad65972f0605983be347f9ccc79
M: 1ed5a33205eac6bcd02672731b8fff55485bd81b 127.0.0.1:6482
   slots:3414-7509,9898-10922 (5121 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.
How many slots do you want to move (from 1 to 16384)? 3072
What is the receiving node ID? 4b420c1a34f8d1053e314c05776fbfc50a3cba5e //6483节点
Please enter all the source node IDs.
  Type 'all' to use all the nodes as source nodes for the hash slots.
  Type 'done' once you entered all the source nodes IDs.
Source node #1:518f740fa4823ad65972f0605983be347f9ccc79//这里我选择了6481节点
Source node #2:done//完成
 

4、观察分配给6483成功了,然后6481同样用方法4096-3071=1025个槽从6482中拿,运行过程就不写了

MacBook-Pro:src healerjean$ ./redis-trib.rb info 127.0.0.1:6481
127.0.0.1:6481 (518f740f...) -> 0 keys | 3071 slots | 1 slaves.
127.0.0.1:6385 (5041f91a...) -> 0 keys | 4096 slots | 1 slaves.
127.0.0.1:6483 (4b420c1a...) -> 0 keys | 4096 slots | 1 slaves.
127.0.0.1:6482 (1ed5a332...) -> 0 keys | 5121 slots | 1 slaves.
[OK] 0 keys in 4 masters.
0.00 keys per slot on average.
MacBook-Pro:src healerjean$ 

5、分配成功

MacBook-Pro:src healerjean$ ./redis-trib.rb info 127.0.0.1:6481
127.0.0.1:6481 (518f740f...) -> 0 keys | 4096 slots | 1 slaves.
127.0.0.1:6385 (5041f91a...) -> 0 keys | 4096 slots | 1 slaves.
127.0.0.1:6483 (4b420c1a...) -> 0 keys | 4096 slots | 1 slaves.
127.0.0.1:6482 (1ed5a332...) -> 0 keys | 4096 slots | 1 slaves.
[OK] 0 keys in 4 masters.
0.00 keys per slot on average.
MacBook-Pro:src healerjean$ 

6、检查槽直接的均匀性,其实通过上面齐刷刷的4096可以看到非常均衡了,但是项目中可能没有这么精确。所以我们还是看看吧 ,同样后面的节点随便选择。他给出了在2%左右,已经相当均衡,无须调整

./redis-trib.rb rebalance  127.0.0.1:6481
healerjean$ ./redis-trib.rb rebalance  127.0.0.1:6481
>>> Performing Cluster Check (using node 127.0.0.1:6481)
[OK] All nodes agree about slots configuration.
>>> Check for open slots...
>>> Check slots coverage...
[OK] All 16384 slots covered.
*** No rebalancing needed! All nodes are within the 2.0% threshold.
MacBook-Pro:src healerjean$ 

4.2、如果不小心加入了两个master节点,6000,6001但是一个需要作为从节点怎么办,在一个上面连个一个选择要做从节点比如6000的客户端状态下执行下面命令

127.0.0.1:6000> cluster replicate 6001Id

4.5、收缩集群

说白了收缩集群就是下线节点

1、首先需要观察下线的节点是否有槽,如果有,则需要将它的槽分给其他节点,保证节点下线后数据的完整性

2、当要下线的节点不在负责槽或者本身是从节点的时候,就可以通知集群让其啊节点忘记下线节点,当所有节点忘记该节点之后就可以正常关闭

1、这里选择下线6381主和6384从节点。则需要将6381的槽平分给其他三个节点 4096/3 = 1365.33 也即 1365 1365 1366。怎么 分配槽就不讲了,看上面的。下面的操作在分配槽完成的基础上 ,可以看到下面的额6481没有槽了

healerjean$ ./redis-trib.rb info 127.0.0.1:6481
127.0.0.1:6481 (518f740f...) -> 0 keys | 0 slots | 0 slaves.
127.0.0.1:6385 (5041f91a...) -> 0 keys | 5462 slots | 2 slaves.
127.0.0.1:6483 (4b420c1a...) -> 0 keys | 5461 slots | 1 slaves.
127.0.0.1:6482 (1ed5a332...) -> 0 keys | 5461 slots | 1 slaves.
[OK] 0 keys in 4 masters.
0.00 keys per slot on average.
MacBook-Pro:src healerjean$ 

2、del-node后面的ip:port只要是cluster中有效节点即可, ,最后一个参数为目标节点的id

./redis-trib.rb del-node  127.0.0.1:6482 518f740fa4823ad65972f0605983be347f9ccc79//6481


./redis-trib.rb del-node  127.0.0.1:6482 96b105f0e33e3c5ccbe5983c7f65f52e7971eac9 //6484
MacBook-Pro:src healerjean$ ./redis-trib.rb del-node  127.0.0.1:6482 518f740fa4823ad65972f0605983be347f9ccc79
>>> Removing node 518f740fa4823ad65972f0605983be347f9ccc79 from cluster 127.0.0.1:6482
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
 
 healerjean$ ./redis-trib.rb del-node  127.0.0.1:6482 96b105f0e33e3c5ccbe5983c7f65f52e7971eac9
>>> Removing node 96b105f0e33e3c5ccbe5983c7f65f52e7971eac9 from cluster 127.0.0.1:6482
>>> Sending CLUSTER FORGET messages to the cluster...
>>> SHUTDOWN the node.
MacBook-Pro:src healerjean$ 

3、访问6481节点发现已经下线

MacBook-Pro:src healerjean$ redis-cli -p 6481
Could not connect to Redis at 127.0.0.1:6481: Connection refused
Could not connect to Redis at 127.0.0.1:6481: Connection refused
not connected> 

4、查看已经在线的状态

127.0.0.1:6482> cluster nodes
4b420c1a34f8d1053e314c05776fbfc50a3cba5e 127.0.0.1:6483 master - 0 1524841422978 13 connected 7510-9556 10923-11947 12288-13652 15360-16383
9c8806f52d7e71c0ef2dc7908879dc84fe40e5c8 127.0.0.1:6485 slave 1ed5a33205eac6bcd02672731b8fff55485bd81b 0 1524841428023 12 connected
5041f91a03330df4c0667b91f11151df965cdaad 127.0.0.1:6385 master - 0 1524841427015 14 connected 0-3413 9557-9897 13653-15359
1ed5a33205eac6bcd02672731b8fff55485bd81b 127.0.0.1:6482 myself,master - 0 0 12 connected 3414-7509 9898-10922 11948-12287
e4b8e373e245e7297a612b56126fd0e01afc7965 127.0.0.1:6386 slave 5041f91a03330df4c0667b91f11151df965cdaad 0 1524841423987 14 connected
42e99055fef2fc54030b1764878925ba878551a8 127.0.0.1:6486 slave 4b420c1a34f8d1053e314c05776fbfc50a3cba5e 0 1524841426511 13 connected
127.0.0.1:6482> 

3、请求路由

3.1、请求重定向

在集群模式下,Redis接收任何键相关的命令首先计算键对应的槽,再跟进槽找出对应的节点,如果是自身。则执行命令,否则回复MOVED重定向错误,通知客户的请求正确的节点,这个过程为MOVED重定向

目前存活的节点是 6482主 6483主 6484 6485 6486 6385主 6386

1、运行6482的客户端,添加一个key,并通过命令查看key所在的槽 ,通过槽可以看到在那个节点,可发现槽并不是连续的,但是很巧它在6482节点

2、为了避免这种偶然性,我又重新设置了一个值,这些不一样了,它给我们返回了了MOVED的槽和所在的节点6483

127.0.0.1:6482> set key:test:1 value-1
OK
127.0.0.1:6482> cluster keyslot  key:test:1
(integer) 5191
127.0.0.1:6482> cluster nodes
4b420c1a34f8d1053e314c05776fbfc50a3cba5e 127.0.0.1:6483 master - 0 1524842246305 13 connected 7510-9556 10923-11947 12288-13652 15360-16383
9c8806f52d7e71c0ef2dc7908879dc84fe40e5c8 127.0.0.1:6485 slave 1ed5a33205eac6bcd02672731b8fff55485bd81b 0 1524842251342 12 connected
5041f91a03330df4c0667b91f11151df965cdaad 127.0.0.1:6385 master - 0 1524842250333 14 connected 0-3413 9557-9897 13653-15359
1ed5a33205eac6bcd02672731b8fff55485bd81b 127.0.0.1:6482 myself,master - 0 0 12 connected 3414-7509 9898-10922 11948-12287
e4b8e373e245e7297a612b56126fd0e01afc7965 127.0.0.1:6386 slave 5041f91a03330df4c0667b91f11151df965cdaad 0 1524842252350 14 connected
42e99055fef2fc54030b1764878925ba878551a8 127.0.0.1:6486 slave 4b420c1a34f8d1053e314c05776fbfc50a3cba5e 0 1524842253360 13 connected
127.0.0.1:6482> set key:test:2 value-2
(error) MOVED 9252 127.0.0.1:6483
127.0.0.1:6482> get key:test:2
(error) MOVED 9252 127.0.0.1:6483
127.0.0.1:6482> 

解释:其实是客户端发送set key:test:2 value-2 给6482。6482计算之后发现不在自己应的槽,所以返回给客户端,客户端在6483节点上重新发送到命令到6483

127.0.0.1:6483> set key:test:2 value-2
OK
127.0.0.1:6483> 

4、Smart客户端

大多数开发语言的Redis客户端都采用Smart客户端支持集群协议,下面是Smarat客户端操作集群的流程。

4.1、首先在JedisCluster初始化的时候选择一个运行节点,初始化槽和节点的映射关系。使用cluster slots命令完成

127.0.0.1:6482> cluster slots
 1) 1) (integer) 7510 //开始槽范围
    2) (integer) 9556//结束槽范围
    3) 1) "127.0.0.1"//主节点ip
       2) (integer) 6483//主节点端口
       3) "4b420c1a34f8d1053e314c05776fbfc50a3cba5e"
    4) 1) "127.0.0.1" //从节点ip
       2) (integer) 6486//从节点端口
       3) "42e99055fef2fc54030b1764878925ba878551a8"
 2) 1) (integer) 10923
    2) (integer) 11947
    3) 1) "127.0.0.1"
       2) (integer) 6483
       3) "4b420c1a34f8d1053e314c05776fbfc50a3cba5e"
    4) 1) "127.0.0.1"
       2) (integer) 6486
       3) "42e99055fef2fc54030b1764878925ba878551a8"
 3) 1) (integer) 12288
    2) (integer) 13652
    3) 1) "127.0.0.1"
       2) (integer) 6483
       3) "4b420c1a34f8d1053e314c05776fbfc50a3cba5e"
    4) 1) "127.0.0.1"
       2) (integer) 6486
       3) "42e99055fef2fc54030b1764878925ba878551a8"
 4) 1) (integer) 15360
    2) (integer) 16383
    3) 1) "127.0.0.1"
       2) (integer) 6483
       3) "4b420c1a34f8d1053e314c05776fbfc50a3cba5e"
    4) 1) "127.0.0.1"
       2) (integer) 6486
       3) "42e99055fef2fc54030b1764878925ba878551a8"
 5) 1) (integer) 0
    2) (integer) 3413
    3) 1) "127.0.0.1"
       2) (integer) 6385
       3) "5041f91a03330df4c0667b91f11151df965cdaad"
    4) 1) "127.0.0.1"
       2) (integer) 6386
       3) "e4b8e373e245e7297a612b56126fd0e01afc7965"
 6) 1) (integer) 9557
    2) (integer) 9897
    3) 1) "127.0.0.1"
       2) (integer) 6385
       3) "5041f91a03330df4c0667b91f11151df965cdaad"
    4) 1) "127.0.0.1"
       2) (integer) 6386
       3) "e4b8e373e245e7297a612b56126fd0e01afc7965"
 7) 1) (integer) 13653
    2) (integer) 15359
    3) 1) "127.0.0.1"
       2) (integer) 6385
       3) "5041f91a03330df4c0667b91f11151df965cdaad"
    4) 1) "127.0.0.1"
       2) (integer) 6386
       3) "e4b8e373e245e7297a612b56126fd0e01afc7965"
 8) 1) (integer) 3414
    2) (integer) 7509
    3) 1) "127.0.0.1"
       2) (integer) 6482
       3) "1ed5a33205eac6bcd02672731b8fff55485bd81b"
    4) 1) "127.0.0.1"
       2) (integer) 6485
       3) "9c8806f52d7e71c0ef2dc7908879dc84fe40e5c8"
 9) 1) (integer) 9898
    2) (integer) 10922
    3) 1) "127.0.0.1"
       2) (integer) 6482
       3) "1ed5a33205eac6bcd02672731b8fff55485bd81b"
    4) 1) "127.0.0.1"
       2) (integer) 6485
       3) "9c8806f52d7e71c0ef2dc7908879dc84fe40e5c8"
10) 1) (integer) 11948
    2) (integer) 12287
    3) 1) "127.0.0.1"
       2) (integer) 6482
       3) "1ed5a33205eac6bcd02672731b8fff55485bd81b"
    4) 1) "127.0.0.1"
       2) (integer) 6485
       3) "9c8806f52d7e71c0ef2dc7908879dc84fe40e5c8"
127.0.0.1:6482> 

4.2、JedisCluser解析Cluster slots结果缓存在本地,并为每个节点创建唯一的jediisPool连接池,映射关系在JedisClusterInfoCacheLEI 中

4.3、后面还有很多,这里就不过多讲解了

5、故障转移

Redis集群本身就实现了高可用

5.1、故障发现

5.1.1、主观下线

当某个节点人为另外一个节点不可用的时候,即下线状态,这个状态并不是最终的故障判定,只能代表一个节点的意见,可能存在误判操作

1、节点a发送ping命令给节点b,如果通信正常将接收到pong消息,节点a更新最近一次与b的通信时间

2、如果a与节点b通信出现问题断开连接,下次会进行重连,如果一直通信失败则通信时间将无法更新

3、当节点通信时间超过cluster-node-timeout时候,更新本地对节点b的状态为主观下线

(每个节点内都会保存其他节点的信息,用于从自身视角判断其他节点的状态)

5.1.2、客观下线

集群内多个节点都认为该节点不可用,从而达成共识,如果是持有槽的主节点故障,需要为它做故障转移。

1、当一个节点判断另外一个节点主观下线的时候,相应的节点状态会通过消息在集群中传播。半数以上的持有槽的主节点都标记某个节点是主观下线时候,触发客观下线流程。

问: 1、为什么必须是负责槽的主节点参与故障发现决策 因为集群模式下只有处理槽的主节点才负责读写请求和集群槽相关的信息维护。

5.2、故障恢复

故障节点变为客观下线后,如果下线节点是持有槽的主节点,则需要在他的从节点中选一个替换它,从而保证截取的高可用。

5.2.1、资格检查

每个从节点都要检查最后与主节点断线的时间,判断是否有个替换故障的主节点,如果从节点与主节点的断线时间超过cluster-node-time*cluster-slave-validity-factor(从节点有效因子默认为10),则当前节点不具备故障转移资格

5.2.2、准备选举时间

从节点符合资格之后,会更新触发故障选举时间,只有到达该时间之后才能执行后续的选举和其他工作,之所以使用延迟触发机制, 主要是通过多个节点使用不同的延迟选举时间来支持优先级的问题,复制偏移量大说明从节点延迟低,那么它就具备更高的优先级来替换主机。也就是说优先级高的延迟时间少,优先发起选举

5.2.3、发起选举

1、更新配置纪元

配置纪元是一个只增不减的张数,每个主节点自身维护一个配置纪元,标注当前主节点的版本,所有主节点的配置纪元都不相等,从节点会复制主节点的配置纪元。整个集群又会维护一个劝全局的配置纪元,用于记录集群内所有主节点配置纪元的最大版本(整个版本可能会由于各种原因增长)。 从节点每次发起投票时都会自增集群的全局配置纪元,用于表示本次从节点发起选举的版本。 cluster info能否可以查看

127.0.0.1:6482> cluster info
cluster_state:ok
cluster_slots_assigned:16384
cluster_slots_ok:16384
cluster_slots_pfail:0
cluster_slots_fail:0
cluster_known_nodes:6
cluster_size:3
cluster_current_epoch:14//整个集群最大配置纪元
cluster_my_epoch:12//当前主节点的配置纪元
cluster_stats_messages_sent:181433
cluster_stats_messages_received:181403
127.0.0.1:6482> 
2、广播选举消息

在集群内广播消息,并记发送消息的状态,保证该从节点在一个配置纪元内只能发起一次选举

5.2.4、投票选举

只有持有槽的主节点才会处理故障转移消息,当第一个从节点请求投票回复成功之后,之后相同配置纪元的其它从节点的选举消息将忽略

所有的节点都只会投给一个从节点,因此只能有一个从节点获取N/2+1的选票,保证能够找出唯一的从节点。每个配置纪元代表了一次选举周期,如果在开始投票之后的cluster-node-timerout*2时间内从及诶单没有获取足够的数量的投票,则本次选举作废。从节点发起配置纪元自增并发起下一轮投票知道选举成功为止

为什么不适用从节点自己进行领导者选举? 这样的话要求从节点数量过多,有点浪费

5.2.5、替换主节点

当前从节点取消复制变成主节点,撤销主节点负责的槽,并将它变成自己的,向其他集群广播自己的pong消息,通过其他节点自己变成了主节点

6、集群运维

6.1、集群完整性

当某个节点在做故障转移的时候,整个集群是不可用的,对于大多数业务无法容忍。因此建议将参数设置为当主节点故障的时候只影响它负责的槽的相关命令,不会影响其他主节点的可用性

cluster-require-full-coverage  no

6.2、宽带消耗

1、适度提高cluseter-node-timeout降低消息发送频率,同时它太高还影响故障转移速度。因此需要根据自身业务场景兼顾二者

2、满足业务的情况下,尽量避免大集群,同一个系统可以针对不同的业务拆分为多个集群。

3、尽量部署到多个机器上。

6.3、pub广播问题

当发布广播的时候,会对所有的集群节点都会发送消息,这样加大了集群的负担。对这种情况,我们尽量使用sentinel结构用于pub、sub功能

6.4、集群倾斜

数据倾斜

1、节点和槽分配不均

2、不同槽对应键数据差异过大,当大量使用hash_tag的时候(因为key是一个,但是value是很多)

3、集合中包含大量元素(–bigkeys)

请求倾斜

1、当大量使用hgetall的时候,尽量使用hmget

6.5、集群读写分离

集群模式下从节点不接受任何读写要求,发送过来的命令会重定向到负责槽的主节点上,当需要使用从节点分担主节点读压力的时候,使用命令 readonly ,之前在复制章节说的slave-read-only也在集群模式下无效。

这个命令属于连接级别,因此每次连接时候都需要执行这个命令,执行readwrite可以关闭连接只读状态

6.6、手动故障转移

cluster failover

6.6.1、强制故障转移

1、当主节点宕机且无法自送完成故障转移的情况,从节点直接发起选举,不在跟主节点确认复制便宜量(从及诶单复制延迟的数据会丢失),从节点请求之后,从节点选举成功后替换为主节点,通知其他

clusetr failover forced

2、用于集群内超过一半主节点故障的场景,因为从节点无法收半数以上投票。所以无法选举。这种命令会不进行选举而是直接进行根系本地配置,可能会冲突配置纪元(多个执行下面的命令) 所以要谨慎

cluster failover takeover

6.7、数据迁移

数据迁移有很多工具,比如唯品会开发的redis-migrate-tool

ContactAuthor