一、前言
redis最初使用主从模式作集群,但是这种模式master宕机后需要手动配置将slave转成master;后来为了高可用,提出了哨兵模式,该模式有哨兵角色监视master和slave,若master宕机,可以自动将slave转换成master,但哨兵模式也有不足:1.不能动态扩充;2.仍然不能支持并发量特别大的场景。所以redis3.x提出来了cluster集群模式。
二、redis-cluster设计
Redis-Cluster采用无中心结构,每个节点报存数据和集群状态,每个节点和其他节点连接。
其结构特点:
- 所有redis节点彼此互联(ping-pong机制),内部使用二进制协议优化传播速度和带宽。
- 节点fail是通过集群中超半数的节点检测失效才失效。
- 客户端与redis节点直连,不需要中间的proxy层。客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可。
- redis-cluster把所有的物理的节点映射到[0-16383]slot上(不一定是平均分配),cluster负责维护node<->slot<->value。
- 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 ....
解决步骤:
- yum install openssl-devel -y
- 在ruby安装包/root/ruby-x.x.x/ext/openssl,执行ruby ./extconf.rb
- 执行make,若出现make: *** No rule to make target
/include/ruby.h', needed by
ossl.o'. Stop.;在Makefile顶部中的增加top_srcdir = ../..
- 执行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含义是设置集群的密码
六、章末小诗
二年寄迹闽山寺,一笑翻然向浙江。
明月不知君已去,夜深还照读书窗