笔者前面的博客分别介绍了Redis“主从复制”和“哨兵模式”服务的搭建,但是这两种服务还有一些问题没有解决,例如:
为了解决这些问题,Redis还提供了一种扩展性更强,可用性更好的Cluster集群方案。
Redis Cluster集群是一个由多个主从节点集群组成的分布式服务器群,它具有数据复制、高可用和数据分片存储的特性。
Redis Cluster集群不需要Sentinel哨兵也能完成节点移除和故障转移的功能。
需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展,官方文档称可以线性扩展到1000个节点。
Redis Cluster集群的性能和可用性均优于“主从复制”和“哨兵模式”,配置也非常简单,更加适用于企业服务。
架构图
如上图所示,本次环境搭建3台Master,6台Slave。
Redis默认有16384个槽位,必须将这些槽位全部分配到Master中,否则会导致有些数据无法写入。
当有数据写入时,Redis使用CRC16算法将Key计算得到一个数字,类似于哈希码,将值与16384取模确定一个槽位,然后将数据写入到该槽位所在的Master节点。
Master写入数据后,会将数据同步到对应的Slave节点,即主从复制。
单台Master宕机后,Redis会自动进行故障转移,将Master下的一台Slave晋升为新Master。
实际应用中Redis节点需要部署到不同的物理机,笔者没有那么多的物理机,遂用单机的不同端口跑多个Redis节点服务。
测试环境:8001、8004、8007为Master。
8002、8003 slaveof 8001
8005、8006 slaveof 8004
8008、8009 slaveof 8007
几个基本命令
拷贝Redis提供的默认配置文件,做一些常规修改(不赘述),搭建集群主要修改如下:
# 启用Cluster集群
cluster-enabled yes
# 保存集群信息的文件,每台节点都会同步集群信息并保存到各自的文件中
cluster-config-file nodes-8001.conf
redis-server 8001/redis.conf
redis-server 8004/redis.conf
redis-server 8007/redis.conf
查看服务是否启动成功:
注意:这时候节点是无法写入数据的,因为没有设置哈希槽Slot,数据不知道该写入哪个节点,如下图:
集群Master节点都虽然已经启动,但是目前节点之间互相还没有关联,互相都不认识,还没有形成集群。
查看集群信息,发现只有自身节点。
通过该命令来让节点互相认识,并形成网状结构。
cluster meet 192.168.1.120 8004
cluster meet 192.168.1.120 8007
除了Master,还得有Slave节点,否则无法实现高可用和数据备份。
启动Slave节点和Master步骤一样,这里不再赘述,启动完成后如下图所示:
启动的节点若不做配置,默认都是Master,需要进行主从分配。
主从分配之前,需要将新启动的Slave节点meet一下,先让节点加入到集群。
cluster replicate node-id
通过该命令来分配主从。
登录到Slave节点执行该命令,node-id为Master节点的id。
查看节点的node-id
将Slave节点分配到Master节点下。(演示8002、8003,其他不赘述)
redis-cli -p 8002 cluster replicate 22bbad3e9cd6065861f13b95cef5d437483c6360
OK
redis-cli -p 8003 cluster replicate 22bbad3e9cd6065861f13b95cef5d437483c6360
OK
前面已经说过,没有为Master指定Slot槽位,是无法写入数据的。
cluster keyslot key
该命令可以查看指定Key的CRC16计算的值。
Redis集群默认有16384个槽位,现有3台Master节点,平均进行分配。
cluster addslots slot [槽位下标]
该命令用来设置节点的槽位。
为了快速分配,写了个Shell脚本,如下:
#!/bin/sh
# 读取端口
echo -n 'port:'
read port
#槽位起始下标
echo -n 'start:'
read s
#槽位结束下标
echo -n 'end:'
read e
#循环设置槽位
for i in $(seq $s $e)
do
redis-cli -h 192.168.1.120 -p $port cluster addslots $i
echo redis-cli -h 192.168.1.120 -p $port cluster addslots $i;
done
指派好槽位后,集群的搭建工作就算完成了。
现在可以正常写入数据了:
以集群的模式去连接:redis-cli -c
Jedis集群写入数据
Set<HostAndPort> hostAndPorts = new HashSet<>(3);
hostAndPorts.add(new HostAndPort("192.168.1.120", 8001));
hostAndPorts.add(new HostAndPort("192.168.1.120", 8004));
hostAndPorts.add(new HostAndPort("192.168.1.120", 8007));
JedisCluster jedisCluster = new JedisCluster(hostAndPorts);
for (int i = 0; i < 100; i++) {
jedisCluster.set(String.valueOf(i), String.valueOf(i));
}
jedisCluster.close();
100个数据会被分散到3台Master里分片存储。
上面记录的是自己原生搭建Redis Cluster集群的方式,稍微有点麻烦,Redis提供了一种更简单的方式来快速搭建。
redis-cli --cluster help
命令会显示一些搭建集群的帮助信息。
快速搭建集群
redis-cli --cluster create host1:port1 ... hostN:portN --cluster-replicas [主从节点的比例]
redis-cli --cluster create 192.168.1.120:8001 192.168.1.120:8002 192.168.1.120:8003 192.168.1.120:8004 192.168.1.120:8005 192.168.1.120:8006 192.168.1.120:8007 192.168.1.120:8008 192.168.1.120:8009 --cluster-replicas 2
查看节点信息
redis-cli工具一条命令即可快速搭建集群,自动分配主从节点,分配槽位。
如果集群的性能达不到预期,可以进行扩容。
Redis Cluster支持线性扩容,官方宣称可以扩容至1000个节点。
扩容命令:redis-cli --cluster add-node new_host:new_port 存在的IP:存在的端口
。
节点默认作为Master被添加,如果需要指明为Slave,加参数:--cluster-slave --cluster-master-id Master的node-id
。
例如将7001、7002、7003添加到集群。
# 7001Master
redis-cli --cluster add-node 192.168.1.120:7001 192.168.1.120:8001
# 7002作为7001的Slave
redis-cli --cluster add-node 192.168.1.120:7002 192.168.1.120:7001 --cluster-slave --cluster-master-id f8bbd9f97db60f4b00a80a2bdcf555e95559582a
# 7003作为7001的Slave
redis-cli --cluster add-node 192.168.1.120:7003 192.168.1.120:7001 --cluster-slave --cluster-master-id f8bbd9f97db60f4b00a80a2bdcf555e95559582a
节点添加后,可以通过cluster nodes
命令查看节点信息。
扩容的Master节点没有默认没有槽位,是不会被写入数据的,需要进行槽位的分配和数据的迁移。
槽位和Key是绑定的,槽位重新分配意味着数据需要重新迁移。
命令:redis-cli --cluster reshard ip port
。
例如:给新加入的7001分配4096个槽位。
redis-cli --cluster reshard 192.168.1.120:8001
How many slots do you want to move (from 1 to 16384)? 4096 #输入分配的槽位数量
What is the receiving node ID? #输入要分配给哪台节点的node-id
Source node #1: all #要从哪些节点取出槽位分配给新节点?all表示从所有节点中平均取
如果集群的性能过剩,也可以选择移除部分节点,Redis也都是支持的。
迁移槽位和数据
注意:移除节点前,需要将该节点的槽位和数据重新分配到其他Master节点!
如果不这么做,会导致数据丢失,以及部分数据写入失败。
将7001的槽位和数据分配到8001上:
–cluster-from 来自哪台节点
–cluster-to 分配到哪台节点去
-cluster-slots 迁移的槽位数量
# 移除7001的槽位和数据
redis-cli --cluster reshard 192.168.1.120:8001 --cluster-from f8bbd9f97db60f4b00a80a2bdcf555e95559582a --cluster-to 201ce4d928c795384bbfde39215065d2eb41592e --cluster-slots 4096
槽位和数据迁移完毕后,就可以移除节点了。
命令:redis-cli --cluster del-node host:port node_id
例如:移除刚添加的7001节点。
# 集群移除节点
redis-cli --cluster del-node 192.168.1.120:8001 f8bbd9f97db60f4b00a80a2bdcf555e95559582a
节点移除后,Redis会自动将其SHUTDOWN
。