Redis集群高可用架构

Posted 小乐乐的天台

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Redis集群高可用架构相关的知识,希望对你有一定的参考价值。

1、Redis高可用集群

1.1、Redis高可用集群模式和哨兵模式方案比较

哨兵模式

上图是Redis的哨兵模式
在redis3.0以前的版本要实现集群一般是借助哨兵sentinel工具来监控master节点的状态,如果master节点异常,则会做主从切换,将某一台slave作为master。哨兵模式一般会有以下几个问题
1、哨兵的配置略微复杂。
2、当主节点挂掉之后,有访问瞬断的情况
3、由于只有一个可写的主节点,没法支持很高的并发。
4、由于只有一个主节点,数据存储能力有限,如果节点内存设置过大,在数据持久化或恢复时会影响性能。

高可用集群模式

redis集群是一个由多个主从节点群组成的分布式服务器群,它具有复制、高可用和分片特性。Redis集群不需要sentinel哨兵也能完成节点移除和故障转移的功能。需要将每个节点设置成集群模式,这种集群模式没有中心节点,可水平扩展,据官方文档称可以线性扩展到上万个节点(官方推荐不超过1000个节点)。redis集群的性能和高可用性均优于之前版本的哨兵模式,且集群配置非常简单。

1.2、Redis高可用集群搭建

redis集群需要至少三个master节点,我们这里搭建三个master节点,并且给每个master再搭建一个slave节点,总共6个redis节点,这里用三台机器部署6个redis实例,每台机器一主一从,搭建集群的步骤如下:


1、修改相应需要的配置,这里我为了规范,修改了很多路径配置:
普通配置

配置含义
daemonize yes默认Redis实例后台启动
port 7001redis实例对外映射端口
pidfile /var/run/redis_7001.pid进程号记录文件路径
logfile “…/logs/7001.log”日志路径
dir …/data/7001/快照文件和集群节点信息文件路径
protected-mode no设置为no,外围机器可以访问
appendonly yes开启aof快照
# bind 127.0.0.1注释掉回环地址
requirepass ale设置redis访问密码
masterauth ale设置集群节点间访问密码

重要配置

配置含义
cluster‐enabled yes启用集群模式
cluster‐config‐file nodes‐8001.conf集群节点信息文件
cluster‐node‐timeout 5000集群访问超时时间

这里粘贴上我修改后的配置文件,可参考
链接:https://pan.baidu.com/s/1_pfVzkIf_vpfkrMmc6N_dQ
提取码:5kcu


2、启动6个redis实例。


3、为了防止防火墙等原因,先在三台服务器上将防火墙关闭,以及禁止开机启动。
systemctl stop firewalld # 临时关闭防火墙
systemctl disable firewalld # 禁止开机启动


4、任意找一台机器的客户端,输入下面命令
这里的 -a 后面跟的是密码,–cluster-replicas 1 后面的 1 代表每个master下面都有一个从节点,默认会将我们命令中的前三个设置为主节点,从节点是随机的。
./redis-cli -a ale --cluster create --cluster-replicas 1 192.168.231.132:7001 192.168.231.133:8001 192.168.231.134:9001 192.168.231.132:7002 192.168.231.133:8002 192.168.231.134:9002
执行完成,如下图所示:

这里的三个段表示我们我们的三个主节点分别对应这三个段,每当我们set一个值时,会先计算这个key的hash值,根据这个位桶数组取模判断是放在哪个节点中,三个节点的数据互相不重复,然后我们输入yes,会出现下图所示:

出现上图现象就代表成功。


6、集群操作:
连接客户端:./redis-cli -a ale -c -h 192.168.231.134 -p 9001
查看集群信息:cluster info
查看节点列表:cluster nodes
关闭集群:./redis-cli -a ale -c -h 192.168.231.134 -p 9001 shutdown

2、Java操作Redis集群

创建一个Maven项目,引入相应的jar包

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>2.9.3</version>
</dependency>
public class JedisClusterTest {
    public static void main(String[] args) throws IOException {

        JedisPoolConfig config = new JedisPoolConfig();
        config.setMaxTotal(20);
        config.setMaxIdle(10);
        config.setMinIdle(5);

        Set<HostAndPort> jedisClusterNode = new HashSet<HostAndPort>();
        jedisClusterNode.add(new HostAndPort("192.168.231.132", 7001));
        jedisClusterNode.add(new HostAndPort("192.168.231.132", 7002));
        jedisClusterNode.add(new HostAndPort("192.168.231.133", 8001));
        jedisClusterNode.add(new HostAndPort("192.168.231.133", 8002));
        jedisClusterNode.add(new HostAndPort("192.168.231.134", 9001));
        jedisClusterNode.add(new HostAndPort("192.168.231.134", 9002));


        JedisCluster jedisCluster = null;
        try {
            //connectionTimeout:指的是连接一个url的连接等待时间
            //soTimeout:指的是连接上一个url,获取response的返回等待时间
            jedisCluster = new JedisCluster(jedisClusterNode, 6000, 5000, 10, "ale", config);
            System.out.println(jedisCluster.set("cluster", "ale"));
            System.out.println(jedisCluster.get("cluster"));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (jedisCluster != null) {
                jedisCluster.close();
            }
        }
    }
}

3、Redis集群原理分析

Redis Cluster 将所有数据划分为 16384 个 slots(槽位),每个节点负责其中一部分槽位。槽位的信息存储于每个节点中。
在每一次连接时,客户端会将集群的信息缓存到本地,这样定位一个节点时就可以定位到是要放到集群的哪个节点上。
但有时候服务端改变了但是客户端没有变,还需要纠正机制来实现槽位信息的校验调整

3.1、槽位定位算法

当我们set一个值的时候,我们会拿到这个值的 key ,对其进行 CRC16 算法,然后再和 16384-1 取 & 运算。
也就相当于和 16384 取模运算。

public static int getSlot(String key) {
    key = JedisClusterHashTagUtil.getHashTag(key);
    return getCRC16(key) & 16383;
}

3.2、跳转重定位

当客户端向一个错误的节点发出了指令,该节点会发现指令的 key 所在的槽位并不归自己管理,这时它会向客户端发送一个特殊的跳转指令携带目标操作的节点地址,告诉客户端去连这个节点去获取数据。客户端收到指令后除了跳转到正确的节点上去操作,还会同步更新纠正本地的槽位映射表缓存,后续所有 key 将使用新的槽位映射表。

3.3、Redis集群节点间的通信机制

redis cluster节点间采取gossip协议进行通信维护集群的元数据(集群节点信息,主从角色,节点数量,各节点共享的数据等)有两种方式:集中式和gossip


集中式:

优点在于元数据的更新和读取,时效性非常好,一旦元数据出现变更立即就会更新到集中式的存储中,其他节点读取的时候立即就可以立即感知到;不足在于所有的元数据的更新压力全部集中在一个地方,可能导致元数据的存储压力。
很多中间件都会借助zookeeper集中式存储元数据。

gossip:

gossip协议包含多种消息,包括ping,pong,meet,fail等等。
meet:某个节点发送meet给新加入的节点,让新节点加入集群中,然后新节点就会开始与其他节点进行通信;
ping:每个节点都会频繁给其他节点发送ping,其中包含自己的状态还有自己维护的集群元数据,互相通过ping交换元数据(类似自己感知到的集群节点增加和移除,hash slot信息等);
pong::对ping和meet消息的返回,包含自己的状态和其他信息,也可以用于信息广播和更新;
fail:某个节点判断另一个节点fail之后,就发送fail给其他节点,通知其他节点,指定的节点宕机了。
gossip协议的优点在于元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新,有一定的延时,降低了压力;缺点在于元数据更新有延时可能导致集群的一些操作会有一些滞后。


gossip通信的10000端口
每个节点都有一个专门用于节点间gossip通信的端口,就是自己提供服务的端口号+10000,比如7001,那么用于节点间通信的就是17001端口。
每个节点每隔一段时间都会往另外几个节点发送ping消息,同时其他节点接收到ping消息之后返回pong消息。

3.4、Redis集群选举原理

当slave发现自己的主节点挂掉之后,就会去选举新的主节点,因为从节点(slave)不止一个,就会出现竞争的关系。
1、slave发现自己的master变为FAIL。
2、将自己记录的集群currentEpoch(选举次数)加1,并广播FAILOVER_AUTH_REQUEST(需要重新选取主节点) 信息。
3、其他节点收到该信息,只有master响应,判断请求者的合法性,并发送FAILOVER_AUTH_ACK(确认合法可以推荐当前从节点为新的主节点),对每一个epoch(从节点)只发送一次ack。
4、从节点收到主节点的确认信息。
5、当收到所有集群节点一半以上时就会把自己置为主节点。
6、slave广播Pong消息通知其他集群节点。


这个时候我们会发现一个问题,就是如果说从节点们都平票了怎么办,平票就需要重新选举,重新走一次上面的流程,那如果每一次都平票了怎么办,其实这种问题redis有自己的一个解决办法,那就是每一个从节点不是一旦感知到主节点没有响应就直接发的,而是会有一个延迟算法,在这个时间经过之后,才会向外发送请求,自荐自己要当主节点。
历史版本有一个延迟时间算法:
DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms
SLAVE_RANK表示此slave已经从master复制数据的总量的rank。Rank越小代表已复制的数据越新。这种方式下,持有最新数据的slave将会首先发起选举(理论上)。

3.5、Redis集群为什么至少需要三个master节点,并且推荐节点数为奇数?

因为新master的选举需要大于半数的集群master节点同意才能选举成功,如果只有两个master节点,当其中一个挂了,是达不到选举新master的条件的。
奇数个master节点可以在满足选举该条件的基础上节省一个节点,比如三个master节点和四个master节点的集群相比,大家如果都挂了一个master节点都能选举新master节点,如果都挂了两个master节点都没法选举新master节点了,所以奇数的master节点更多的是从节省机器资源角度出发说的。

3.6、Redis集群对批量操作命令的支持

我们正常使用 mset 来批量设置值的时候,如果计算出多个key值的slot值不相等,则会直接报错,如下图所示:

Redis集群给我们提供了一个大括号,当我们加上这个大括号之后,计算solt值时就只会计算大括号里面的数据,就是{person}的hash值,如下图所示:

4、Redis集群可能会遇到的问题

4.1、网络抖动

所谓的网络抖动就是集群中由于网络问题突然从节点就和主节点发生了瞬断,这个时候按照原理来说的话就应该重新选取主节点了,但是这个时候主节点不一定是挂掉,可能只是在很短的时间里网络原因没有联通而已,所以redis给我们提供了 cluster-node-timeout 参数,可以设置时间,当超过这个时间还是没有连接上,就判定这个节点挂了。
如果没有这个参数,一旦访问不同主节点就去选举,就会造成一个集群节点中出现多个主节点,这就会产生脑裂问题。

4.2、集群脑裂数据丢失问题

当一个集群节点中出现了多个主节点时,如果这两个主节点都可以写数据,然后过了一段时间,网络恢复,原来的那个主节点又变成了从节点,那么在这一段时间写到原来主节点的数据就会丢失,redis对其的解决办法就是配置下面这一个信息:
解决方案:
min-replicas-to-write 1
// 写数据成功最少同步的slave数量,如果达不到这个数量,就不会写成功。

4.3、集群是否完整才能对外提供服务

这里其实涉及到一个参数。
当redis.conf的配置cluster-require-full-coverage为no时,意思是大集群里面有好多小集群,当有一个小集群的主节点挂掉之后,已经没有从节点给它顶上的时候,说明这个小集群已经瘫痪掉,当上面的配置为no时,整个的集群还是可用的,如果为yes,整个集群就不能用了。

以上是关于Redis集群高可用架构的主要内容,如果未能解决你的问题,请参考以下文章

架构设计:系统存储(17)——Redis集群方案:高可用

redis高可用架构

Redis高可用架构之Cluster集群和分⽚

高可用集群架构——redis的主从复制与哨兵模式,cluster

Redis高可用架构-Redis集群,一文get三种方式

架构师修炼之路Redis 哨兵机制 ( Sentinel ) : 实现高可用Redis 哨兵机制 ( Sentinel ) : 实现高可用...