Redis Cluster

Redis Cluster要求至少需要3个master才能组成一个集群,同时每个master至少需要有一个slave节点。各个节点之间保持TCP通信。当master发生了宕机, Redis Cluster自动会将对应的slave节点提拔为master,来重新对外提供服务。

Redis Cluster 功能 : 负载均衡,故障切换主从复制

负载均衡

先说下槽,集群中每个redis实例都负责接管一部分的槽,总槽树为16384,(2^14)如果有三台master,那么负责6461个槽

redis节点负责的槽位
节点10-5461
节点25461-10922
节点310922-16383

当redis客户端设置值时,会拿key进行CRC16算法,然后 跟16384取模,得到的就是落在哪个槽位,根据上面表格就得出在哪台节点上。槽公式如下:

slot = CRC16(key) & 16383

Redis集群中,每个节点都会有其余节点ip,负责的槽 等 信息。

JedisCluster如何寻址的

JedisCluster配置只用指定集群中某一个节点的IP,端口信息就可以了。JedisCluster初始化时,会找配置的节点获取整个集群的信息(cluster nodes命令)

解析集群信息,得到集群中所有matser的信息,如何遍历每台master通过ip,端口构建jedis实例,如何put到一个全局nodes变量里(map类型),key为端口,值为Jedis实例,如下

nodes={172.19.93.120:6380=redis.clients.jedis.JedisPool@74ad1f1f,.....}

在上面遍历master过程中,还做一件事,遍历此台master负责的槽索引,然后又put到一个全局map slots里面。值为上面的Jedis实例, slots值如下:

slots={0=redis.clients.jedis.JedisPool@74ad1f1f,
1=redis.clients.jedis.JedisPool@74ad1f1f,
2=redis.clients.jedis.JedisPool@74ad1f1f,
....
5461 = redis.clients.jedis.JedisPool@65aa1f2f,    ####另外的master机器
....
16383=redis.clients.jedis.JedisPool@756d1afd}

有了上面的slots变量,当有值set时,会先算出slot = getCRC16(key)&(16383-1),加入是12182,然后调用slots.get(12182)得到Jedis实例,然后去操作redis

如果发现MovedDataException,说明初始化得到的槽位与节点的对应关系有问题,(节点新增或者宕机)就会重置slots。

集群机器之间的通信

集群机器等数据信息通常有两种方式,一种是集中式,比如springcloud服务集群信息保存在配置中心 。另一种就是redis的方式,gossip。

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

gossip:好处在于,元数据的更新比较分散,不是集中在一个地方,更新请求会陆陆续续,打到所有节点上去更新,有一定的延时,降低了压力; 缺点,元数据更新有延时,可能导致集群的一些操作会有一些滞后。

通信的端口就是本身redis监听端口+10000 ,比如 监听端口6379,通信端口就是16379 。

Gossip协议的主要职责就是信息交换。信息交换的载体就是节点彼此发送的Gossip消息,常用的Gossip消息可分为:ping消息、pong消息、meet消息、fail消息等。

  • meet消息:用于通知新节点加入。消息发送者通知接收者加入到当前集群,meet消息通信正常完成后,接收节点会加入到集群中并进行周期性的ping、pong消息交换。
  • ping消息:集群内交换最频繁的消息,集群内每个节点每秒向多个其他节点发送ping消息,用于检测节点是否在线和交换彼此状态信息。ping消息发送封装了自身节点和部分其他节点的状态数据。
  • pong消息:当接收到ping、meet消息时,作为响应消息回复给发送方确认消息正常通信。pong消息内部封装了自身状态数据。节点也可以向集群内广播自身的pong消息来通知整个集群对自身状态进行更新。
  • fail消息:当节点判定集群内另一个节点下线时,会向集群内广播一个fail消息,其他节点接收到fail消息之后把对应节点更新为下线状态。

举例当新增一个节点,也就是Meet消息过程

  • 节点A会为节点B创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。
  • 节点A根据CLUSTER MEET命令给定的IP地址和端口号,向节点B发送一条MEET消息。
  • 节点B接收到节点A发送的MEET消息,节点B会为节点A创建一个clusterNode结构,并将该结构添加到自己的clusterState.nodes字典里面。
  • 节点B向节点A返回一条PONG消息。
  • 节点A将受到节点B返回的PONG消息,通过这条PONG消息节点A可以知道节点B已经成功的接收了自己发送的MEET消息。
  • 之后,节点A将向节点B返回一条PING消息。
  • 节点B将接收到的节点A返回的PING消息,通过这条PING消息节点B可以知道节点A已经成功的接收到了自己返回的PONG消息,握手完成。
  • 之后,节点A会将节点B的信息通过Gossip协议传播给集群中的其他节点,让其他节点也与节点B进行握手,最终,经过一段时间后,节点B会被集群中的所有节点认识。

举例当一个节点故障,怎么判断下线

集群中的每个节点都会定期向其他节点发送ping命令,如果接受ping消息的节点在指定时间内没有回复pong,则发送ping的节点就把接受ping的节点标记为主观下线

如果集群半数以上的主节点都将主节点A标记为主观下线,则节点A将被标记为客观下线(通过节点的广播)即下线。

故障切换

当一个从节点发现自己正在复制的主节点进入了已下线状态时,从节点将开始对下线主节点进行故障转移,以下是故障转移执行的步骤:

  • 从节点会执行SLAVEOF no one命令,成为新的主节点;
  • 新的主节点会撤销所有对已下线主节点的槽指派,并将这些槽全部指派给自己;
  • 新的主节点向集群广播一条PONG消息,这条PONG消息可以让集群中的其他节点立即知道这个节点已经由从节点变成了主节点,并且这个主节点已经接管了原本由已下线节点负责处理的槽。
  • 新的主节点开始接收和自己负责处理的槽有关的命令请求,故障转移完成。

总结

优势

  • 无中心架构。
  • 数据按照 slot 存储分布在多个节点,节点间数据共享,可动态调整数据分布。
  • 可扩展性,可线性扩展到 1000 个节点(官方推荐不超过 1000 个),节点可动态添加或删除。
  • 高可用性,部分节点不可用时,集群仍可用。通过增加 Slave 做 standby 数据副本,能够实现故障自动 failover,节点之间通过 gossip 协议交换状态信息,
  • 投票机制完成 Slave 到 Master 的角色提升。
  • 降低运维成本,提高系统的扩展性和可用性。

不足

  • Client 实现复杂,驱动要求实现 Smart Client,缓存 slots mapping 信息并及时更新,提高了开发难度,客户端的不成熟影响业务的稳定性。
  • 节点会因为某些原因发生阻塞(阻塞时间大于 clutser-node-timeout),被判断下线,这种 failover 是没有必要的。
  • 数据通过异步复制,不保证数据的强一致性。
  • 多个业务使用同一套集群时,无法根据统计区分冷热数据,资源隔离性较差,容易出现相互影响的情况。
  • Slave 在集群中充当 “冷备”,不能缓解读压力,当然可以通过 SDK 的合理设计来提高 Slave 资源的利用率。

Codis

Codis 是一个分布式 Redis 解决方案,对于上层的应用来说,连接到 Codis Proxy 和连接原生的 Redis Server 没有明显的区别 (不支持的命令列表), 上层应用可以像使用单机的 Redis 一样使用,Codis 底层会处理请求的转发,不停机的数据迁移等工作,所有后边的一切事情,对于前面的客户端来说是透明的,可以简单的认为后边连接的是一个内存无限大的 Redis 服务。

如下图所示,表示 Codis 的整体架构图。

Codis Proxy: 客户端连接的 Redis 代理服务,实现了 Redis 协议。 除部分命令不支持以外 (不支持的命令列表),表现的和原生的 Redis 没有区别(就像 Twemproxy)。对于同一个业务集群而言,可以同时部署多个 codis-proxy 实例;不同 codis-proxy 之间由 codis-dashboard 保证状态同
codis-redis-group: 代表一个 redis 服务集群节点,一个 RedisGroup 里有一个 Master,和多个 Slave

Zookeeper:Codis 依赖 ZooKeeper 来存放数据路由表和 codis-proxy 节点的元信息,codis-config 发起的命令都会通过 ZooKeeper 同步到各个存活的 codis-proxy。

Last modification:November 17, 2023
如果觉得我的文章对你有用,请随意赞赏