Bootstrap

Redis Cluster集群的搭建与测试

一、前言

redis最初使用主从模式作集群,但是这种模式master宕机后需要手动配置将slave转成master;后来为了高可用,提出了哨兵模式,该模式有哨兵角色监视master和slave,若master宕机,可以自动将slave转换成master,但哨兵模式也有不足:1.不能动态扩充;2.仍然不能支持并发量特别大的场景。所以redis3.x提出来了cluster集群模式。

二、redis-cluster设计

Redis-Cluster采用无中心结构,每个节点报存数据和集群状态,每个节点和其他节点连接。

其结构特点:

  1. 所有redis节点彼此互联(ping-pong机制),内部使用二进制协议优化传播速度和带宽。
  2. 节点fail是通过集群中超半数的节点检测失效才失效。
  3. 客户端与redis节点直连,不需要中间的proxy层。客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
  4. redis-cluster把所有的物理的节点映射到[0-16383]slot上(不一定是平均分配),cluster负责维护node<->slot<->value。
  5. Redis集群预分好16384个桶,当需要在集群放置key-value时,根据CRC16(key) mod 16384的值,决定将一个key放到哪个桶中。

a.redis cluster节点分配
现在我们是三个主节点分别是:A, B, C 三个节点,它们可以是一台机器上的三个端口,也可以是三台不同的服务器。那么,采用哈希槽 (hash slot)的方式来分配16384个slot 的话,它们三个节点分别承担的slot 区间是:

  • 节点A覆盖0-5460;

  • 节点B覆盖5461-10922;

  • 节点C覆盖10923-16383.

    获取数据:
    如果存入一个值,按照redis cluster哈希槽的算法: CRC16('key')384 = 6782。 那么就会把这个key 的存储分配到 B 上了。同样,当我连接(A,B,C)任何一个节点想获取'key'这个key时,也会这样的算法,然后内部跳转到B节点上获取数据

    新增一个主节点:
    新增一个节点D,redis cluster的这种做法是从各个节点的前面各拿取一部分slot到D上,我会在接下来的实践中实验。大致就会变成这样:

  • 节点A覆盖1365-5460

  • 节点B覆盖6827-10922

  • 节点C覆盖12288-16383

  • 节点D覆盖0-1364,5461-6826,10923-12287

同样删除一个节点也是类似,移动完成后就可以删除这个节点了。

 b.Redis Cluster主从模式

redis-cluster为了保证数据的高可用性,加入了主从模式,一个主节点对应多个从节点,主节点提供数据存取,从节点从主节点拉取数据,当主节点挂掉以后,就会由从节点替换主节点,保证集群不会挂掉。

上面那个例子里, 集群有ABC三个主节点, 如果这3个节点都没有加入从节点,如果B挂掉了,我们就无法访问整个集群了。A和C的slot也无法访问。

所以我们在集群建立的时候,一定要为每个主节点都添加了从节点, 比如像这样, 集群包含主节点A、B、C, 以及从节点A1、B1、C1, 那么即使B挂掉系统也可以继续正确工作。

B1节点替代了B节点,所以Redis集群将会选择B1节点作为新的主节点,集群将会继续正确地提供服务。 当B重新开启后,它就会变成B1的从节点。

不过需要注意,如果节点B和B1同时挂了,Redis集群就无法继续正确地提供服务了。

三、redis集群的搭建

集群中至少应该有奇数个节点,所以至少有三个节点,每个节点至少有一个备份节点,所以下面使用6节点(主节点、备份节点由redis-cluster集群确定)。

3.1 安装redis节点,与设置端口

(1)Redis集群最少需要6个节点,可以分布在一台或者多台主机上。

本教案在一台主机上创建伪分布式集群(实际主从节点不应该在一台服务器上),不同的端口表示不同的redis节点,如下:

主节点:127.0.0.1:7000 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

a.创建redis-cluster目录,解压redis压缩包,编译安装

b.在redis-cluster目录创建如下目录:

7000 7001 7002 7003 7004 7005六个目录(再次提醒,这是将集群部署在同一台机器的伪集群,实际生产中主从不应该在一台机器上),bin,data目录

c.将redis解压后的src目录下的这这些文件复制到redis-cluster/bin目录下,后面的命令执行都在这个目录下操作:

(2)在7000中创建配置文件7000.conf(7001、7002、7003、7004、7005除端口外同样的配置),内容如下:

#后台启动
daemonize yes
#修改端口号,从7001到7006
port 7000
#开启cluster,去掉注释
cluster-enabled yes
#自动生成
#节点通信时间
cluster-config-file nodes_7000.conf
cluster-node-timeout 15000
#持久化方式
appendonly yes
pidfile /var/run/redis_7000.pid
logfile "/usr/local/redis-cluster/7000/log/redis_7000.log"

protected-mode no
#requirepass "123456"
#bind 47.110.12.102

 为什么注释掉了requirepass和bind,后面会有解释

3.2 安装redis-trib所需的 ruby脚本

注意:centos7默认的ruby版本太低(2.0),要卸载重装(最低2.2)

a.所以我们先要升级ruby,请参考CentOS7如何升级ruby版本 。

b.复制redis解压文件src下的redis-trib.rb文件到redis-cluster目录并安装gem:

gem install redis-4.1.0.gem

若不想安装src目录下的gem,也可以直接gem install redis

注意,gem install可能会报错
Unable to require openssl,install OpenSSL and rebuild ruby (preferred) or use ....
解决步骤:

  1. yum install openssl-devel -y
  2. 在ruby安装包/root/ruby-x.x.x/ext/openssl,执行ruby ./extconf.rb
  3. 执行make,若出现make: *** No rule to make target /include/ruby.h', needed byossl.o'. Stop.;在Makefile顶部中的增加top_srcdir = ../..
  4. 执行make install

 3.3.启动所有的redis节点

可以写一个命令脚本start-all.sh:

cd 7000
../bin/redis-server 7000.conf
cd ../
cd 7001
../bin/redis-server 7001.conf
cd ../
cd 7002
../bin/redis-server 7002.conf
cd ../
cd 7003
../bin/redis-server 7003.conf
cd ../
cd 7004
../bin/redis-server 7004.conf
cd ../
cd 7005
../bin/redis-server 7005.conf

设置权限启动

[root@localhost redis-cluster]# chmod 777 start-all.sh 
[root@localhost redis-cluster]# ./start-all.sh 

查看redis进程启动状态

[root@localhost redis-4.0.2]# ps -ef|grep cluster
root      54956      1  0 19:17 ?        00:00:00 redis-server *:7000 [cluster]
root      54961      1  0 19:17 ?        00:00:00 redis-server *:7001 [cluster]
root      54966      1  0 19:17 ?        00:00:00 redis-server *:7002 [cluster]
root      54971      1  0 19:17 ?        00:00:00 redis-server *:7003 [cluster]
root      54976      1  0 19:17 ?        00:00:00 redis-server *:7004 [cluster]
root      54981      1  0 19:17 ?        00:00:00 redis-server *:7005 [cluster]
root      55071  24089  0 19:24 pts/0    00:00:00 grep --color=auto cluster

可以看到redis的6个节点已经启动成功
注意:这里并没有创建集群

四、使用redis-trib.rb创建集群

注意:redis-trib.rb在redis/src目录下。

创建集群命令如下:

./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:7000

使用create命令 --replicas 1 参数表示为每个主节点创建一个从节点,其他参数是实例的地址集合。

创建集群成功:

[root@iZbp1i9fu1a7yjd42innezZ bin]# ./redis-trib.rb create --replicas 1 127.0.0.1:7000 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
>>> Creating cluster
>>> Performing hash slots allocation on 6 nodes...
Using 3 masters:
127.0.0.1:7000
127.0.0.1:7001
127.0.0.1:7002
Adding replica 127.0.0.1:7003 to 127.0.0.1:7000
Adding replica 127.0.0.1:7004 to 127.0.0.1:7001
Adding replica 127.0.0.1:7005 to 127.0.0.1:7002
M: 2f57fb639b0006b5109d3449b0b3391af47f8bbf 127.0.0.1:7000
   slots:0-5460 (5461 slots) master
M: b55f678ec274486abab3e5e5881fd04facf44f81 127.0.0.1:7001
   slots:5461-10922 (5462 slots) master
M: af1b42f67700fe366ae8b49634241a7ff603f85e 127.0.0.1:7002
   slots:10923-16383 (5461 slots) master
S: 837c2d4c5995142b89b0bb3cd787df2ee67c7390 127.0.0.1:7003
   replicates 2f57fb639b0006b5109d3449b0b3391af47f8bbf
S: d6b2f6724df78a9bbb70f3fc036650bafafcae9f 127.0.0.1:7004
   replicates b55f678ec274486abab3e5e5881fd04facf44f81
S: c6676101561bcf9581c8d63e324af2fc8581c101 127.0.0.1:7005
   replicates af1b42f67700fe366ae8b49634241a7ff603f85e
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:7000)
M: 2f57fb639b0006b5109d3449b0b3391af47f8bbf 127.0.0.1:7000
   slots:0-5460 (5461 slots) master
   1 additional replica(s)
S: 837c2d4c5995142b89b0bb3cd787df2ee67c7390 127.0.0.1:7003
   slots: (0 slots) slave
   replicates 2f57fb639b0006b5109d3449b0b3391af47f8bbf
S: c6676101561bcf9581c8d63e324af2fc8581c101 127.0.0.1:7005
   slots: (0 slots) slave
   replicates af1b42f67700fe366ae8b49634241a7ff603f85e
S: d6b2f6724df78a9bbb70f3fc036650bafafcae9f 127.0.0.1:7004
   slots: (0 slots) slave
   replicates b55f678ec274486abab3e5e5881fd04facf44f81
M: af1b42f67700fe366ae8b49634241a7ff603f85e 127.0.0.1:7002
   slots:10923-16383 (5461 slots) master
   1 additional replica(s)
M: b55f678ec274486abab3e5e5881fd04facf44f81 127.0.0.1:7001
   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.

 从上面可以看出:

  • master节点为:127.0.0.1:7000(槽为0-5460),127.0.0.1:7001(槽为5461-10922),127.0.0.1:7002(槽为10923-16383)
  • 127.0.0.1:7003为127.0.0.1:7000的从节点,127.0.0.1:7004为127.0.0.1:7001的从节点,127.0.0.1:7003为127.0.0.1:7005的从节点。

4.2 创建集群这一步可能会出错

a.can't connect to node 127.0.0.1:7001

解决方法:各节点redis配置去掉了bind 和 requestpass配置。
这里需要提一点,集群如果要设置密码,要用masterauth配置而部署requestpass。

参考文章:redis创建集群——[ERR] Sorry, can't connect to node 192.168.X.X
Redis Cluster 设置密码

b.Node 127.0.0.1:7000 is not empty.

解决方法:删除生成的配置文件nodes.conf,如果不行则说明现在创建的结点包括了旧集群的结点信息,需要删除redis的持久化文件后再重启redis,比如:appendonly.aof、dump.rdb

参考文章:Node 127.0.0.1:7000 is not empty. Either the node already knows other nodes (check with CLUSTE

五、redis集群的测试

5.1 查看集群主从关系:../bin/redis-cli -h 127.0.0.1 -p 7000 cluster nodes

5.2 测试存取值,客户端连接集群redis-cli需要带上 -c ,redis-cli -c -p 端口号

./bin/redis-cli -c -h 127.0.0.1 -p 7000

 

从上图可以看出来,虽然我连接7000 set数据,但如果经过算法该key落到 [9189],属于7001范围,它会转向7001插入值。

5.3 kill掉7000的redis,7003是否会顶上

上述操作可用看出,7003取代了7000担任了master角色。

5.4 重启7000,是否会自动恢复

执行:

 ../bin/redis-server 7000.conf 

7000重启成功。接着查看7000的集群状态:

有的文章说重启7000节点变成了7003的从节点,我们看一下:

发现它的集群只有自己一个节点。所以他是没有加入之前的集群的。

而我查看没有挂掉的节点的集群状态,还保持在7000未重启之前的状态:

现在我再操作7000,会报错:

(error) CLUSTERDOWN Hash slot not served

 

为什么没有如其他文章所说自动加入集群,这里还有待深入。。。

注:很多文章会提到--cluster命令,--cluste是在redis5.0以上的版本才支持,本文章用的版本是4.0.1。

   masterauth 123456:masterauth含义是设置集群的密码

 六、章末小诗

二年寄迹闽山寺,一笑翻然向浙江。

明月不知君已去,夜深还照读书窗

;