一、Redis集群搭建二、Jedis连接Redis集群三、Redis集群原理
1. 槽位定位算法2. 跳转重定位3. Redis集群之间的通讯机制4. 网络抖动5. Redis集群选举原理6. 集群脑裂数据丢失问题7. Redis集群为什么至少需要三个,并且推荐节点数为奇数
一、Redis集群搭建Redis集群是由一个或多个主从节点群组成的分布式服务器群,具有复制,高可用和分片的特性。Redis集群不需要哨兵就能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展
准备工作:由于Redis集群至少需要三个主节点,所以准备三个Redis主节点,每个主节点配一个从节点共6个Redis实例,演示环境为:三台虚拟机,每个虚拟机启动2个Redis实例,Redis单机安装可以参考:Redis单机安装
以111虚拟机为例,新建8001,8002目录
复制Redis.conf到指定目录并修改Redis.conf配置文件
daemonize yes port 8001 # 把pid进程号写入pidfile配置的文件 pidfile /var/run/redis_8001.pid # 指定数据文件存放位置,必须要指定不同的目录位置,不然会丢失数据 dir /usr/local/redis/8001/ # 启动集群模式 cluster‐enabled yes # 集群节点信息文件,这里800x最好和port对应上 cluster‐config‐file nodes‐8001.conf cluster‐node‐timeout 10000 # bind 127.0.0.1 # 关闭保护模式 protected‐mode no appendonly yes # 设置redis访问密码 requirepass maojian # 设置集群节点间访问密码,跟上面一致 masterauth zhuge
其他5个节点修改配置文件的相关端口就可以了
6个节点准备好之后,启动6个Redis实例
此时启动的6个节点ip端口分别为:
192.168.1.111:8001
192.168.1.111:8002
192.168.1.112:8001
192.168.1.112:8002
192.168.1.113:8001
192.168.1.114:8002
用redis-cli命令创建整个redis集群
# 命令中的1指的是为每个创建的主节点创建一个从节点 # 执行这条命令需要三台机器之间的Redis实例能相互访问,可以关闭防火墙或者打开redis服务端口和集群节点gossip通信端口(默认在Redis端口上加10000) redis-cli -a maojian --cluster create --cluster-replicas 1 192.168.1.111:8001 192.168.1.112:8001 192.168.1.113:8001 192.168.1.111:8002 192.168.1.112:8002 192.168.1.113:8002
验证集群是否启动成功
public class JedisClusterTest {
@Test
public void testCluster() throws IOException {
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
jedisPoolConfig.setMaxTotal(20);
jedisPoolConfig.setMaxIdle(10);
jedisPoolConfig.setMinIdle(5);
Set jedisClusterNode = new HashSet<>();
jedisClusterNode.add(new HostAndPort("192.168.1.111", 8001));
jedisClusterNode.add(new HostAndPort("192.168.1.111", 8002));
jedisClusterNode.add(new HostAndPort("192.168.1.112", 8001));
jedisClusterNode.add(new HostAndPort("192.168.1.112", 8002));
jedisClusterNode.add(new HostAndPort("192.168.1.113", 8001));
jedisClusterNode.add(new HostAndPort("192.168.1.113", 8002));
JedisCluster jedisCluster = null;
try {
jedisCluster = new JedisCluster(jedisClusterNode, 6000, 5000, 10, "maojian", jedisPoolConfig);
System.out.println(jedisCluster.set("cluster:1", "1"));
System.out.println(jedisCluster.get("cluster:1"));
} catch (Exception e) {
e.printStackTrace();
} finally {
if (jedisCluster != null) {
jedisCluster.close();
}
}
}
}
运行结果:
Redis Cluster将所有的数据划分为16384个slots(槽位),每个节点负责其中一部分槽位。槽位的信息存储每个节点中
当Redis Cluster的客户端来连接集群时,它也会得到一份集群的槽位配置信息并将其缓存到客户端本地,这样客户端要操作某个key时,可以直接定位到目标节点。槽位的信息可能会存在客户端与服务端不一致的情况,还需要纠正机制来实现槽位信息的校验调整
Cluster默认会对key使用crc16算法进行hash得到一个整数值,用这个值对16384进行取模得到具体的槽位
HASH_SOLT = CRC16(key) mod 16384
当客户端向一个错误的节点发送指令,改节点会发现指令的key所在的槽位并不归自己管理,这是它会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连接这个节点去操作。客户端接到这个指令后除了跳转到正确的节点去操作,还会同步纠正本地的槽位映射表缓存,后续所有的key将使用新的槽位
Redis cluster节点间才去gossip协议进行通信
维护集群的元数据(集群节点信息,主从角色,节点数量,各节点共享的数据)有两种方式,集中式和gossip。
集中式:优点在于元数据更新和读取,时效性非常好,一旦元数据出现变更立即就会更新到集中式的存储中,其他节点读取的时候就会立即读取到,缺点是,所有的元数据
更新压力全部集中在一个地方,可能导致元数据的存储压力,很多中间件都会借助zookeeper集中式存储数据gossip:优点在于去中心化,降低了节点的压力,缺点是更新会有一定的延迟性
4. 网络抖动
机房网络经常会发生各种各样的一些小问题,网络抖动就是一种非常常见的现象,突然之间部分连接变得不可以访问,然后又会很快恢复正常
为解决这种问题,Redis Cluster提供了一种选项,cluster-node-timeout,表示当某个节点持续timeout的时间失联时,才可以认定改节点出现故障,需要进行主从切换。如果没有这个选项,网络抖动会导致频繁的主从切换
5. Redis集群选举原理当slave发现自己的master变为FAIL状态时,便尝试进行Failover,希望让自己成为新的master,由于挂掉的master可能会有多个slave,从而存在多个slave竞争成为master节点的过程,其过程如下:
- slave发现自己的master变为FAIL将自己记录的currentEpoch加1,并广播FAILOVER_AUTH_REQUEST信息其他节点收到该信息,只有master响应,判断请求者的合法性,并发送FAILOVER_AUTH_ACK,对每一个epoch只发送一次ack尝试failover的slave收集master返回的FAILOVER_AUTH_ACKslave收到超过半数的master的ack后变成新的master(集群至少三个的原因是,如果只有两个的话,其中一个挂了,只剩一个节点是无法选举成功的)
Redis集群没有过半机制会有脑裂问题,网络分区导致脑裂后多个主节点对外提供服务,一旦网络分区恢复,会将其中一个主节点变为从节点,这时会有大量数据丢失
解决方法是:在redis.conf配置文件中加上参数(不能100%避免数据丢失)
# 写数据成功最少同步的slave数量, min-replicas-to-write 1
这个配置在一定的程度上降低了集群的可用性,比如slave要是少于一个,这个集群就算leader正常也不能提供服务了,具体场景具体决策
7. Redis集群为什么至少需要三个,并且推荐节点数为奇数因为新的主节点需要大于半数的主节点同意才能选举成功,如果只有两个master节点,当其中一个挂了,是达不到选举新主节点的条件的
奇数个主节点可以在满足改条件的基础上节省一个节点,比如三个主节点和四个主节点的集群相比,大家都挂一个节点都能选举成功,挂了两个都无法选举成功,所以奇数个主节点能节省机器资源



