redis — 集群模式与原理介绍(一)

1 简介

Redis 提供数据缓存服务,内部数据都存在内存中,所以访问速度非常快。

服务模式

1.1 主从模式/单机模式

早期,Redis单应用服务亦能满足企业的需求。之后,业务量的上升,单机的读写能力满足不了业务的需求,技术上实现主从服务,并读写分离,分担主 Master 的读负担。
Redis 单机模式下,即便是“1主 N 备”结构,当主节点故障时,备节点也无法自动升主,即无法自动故障转移(Failover)。
redis — 集群模式与原理介绍(一)_第1张图片

1.2 哨兵模式

故障转移需要“哨兵”Sentinel 辅助,Sentinel 是 Redis 高可用的解决方案,由一个或者多个 Sentinel 实例组成的系统可以监视 Redis 主节点及其从节点,当检测到 Redis 主节点下线时,会根据特定的选举规则从该主节点对应的所有从节点中选举出一个“最优”的从节点升主,然后由升主的新主节点处理请求。
redis — 集群模式与原理介绍(一)_第2张图片
哨兵本身是一个小集群,Redis 本身为一主多从。哨兵模式只提供单一节点(主节点)对外服务,当主节点出现问题时,将出现瞬断问题,在进行选举时,不能提供服务;
哨兵两两通讯,Redis 两两通讯,哨兵与 Redis 间两两通讯。哨兵与 Redis 节点间,都是通过心跳来监控状态,哨兵本身就是一个小集群;
每个哨兵监控所有主从节点。哨兵监控着 Redis 的运行状况,确定主节点以及从节点,保存清单,当主节点挂掉时,进行主节点的选举,更新服务列表;
Server 首先通过哨兵(Sentinel)来确定访问那台 Redis 服务器 服务端使用 Redis 时,优先访问哨兵集群,获取可访问的 Redis 服务的 IP 和端口。

1.3 集群模式

redis — 集群模式与原理介绍(一)_第3张图片
集群对外统一。在使用集群时,只需要关注 Redis 各个节点的 IP 和端口,至于读写节点、主从等无需关注;
集群内部协调。主从节点在集群部署时已选举完成,除非节点挂掉,会进行重新选举。其次删除相关配置项时,也会重新选举主节点;
去中心化。本模式不再如哨兵、Codise 等,需要第三方监控。Cluster自行加入选举,完成主节点选举,以及读写访问控制。

原理介绍
  • 选举
    集群启动后,主从已分配完成,经过了 N 轮的选举。当某一个主节点宕机,那么从节点需要经过选举成为主节点。简单介绍选举过程:
    所有子节点向其他节点发送请求,请求自身成为主节点,其他节点收到请求后,返回投票信息,只有主节点 master 有权投票,且只能投一次,当获取到的票数大于一半人数时(master 个数),就当选 master。
    期间,所有子节点发送请求的时机有所有不同。所以基本都有先后顺序,所以很少会出现票数相同情况,如果相同,则重新选举,直到选出 master 为止。
    故,需要至少 3 主 3 从,否则节点出现问题,将选举失败。
  • 槽位
    在 Redis 集群中,定义了 16384 个逻辑上的槽位。将这些槽位均匀分配给 N 个节点(一主一从为一个节点),此文 3 个节点,自动均匀分配。意思为,0-5460 个槽位分配给第一个节点。
    当用户 set 一个值时,除了计算 key 本身的 hashCode 之外,还会调用 C 语言的一个 CRC16 算法,将 key 当 hash 值再计算出一个数字,然后与 16384 取模,得到的数字落在哪个槽位,则会将数据放在对应的节点。
    比如,计算出的数字为 16387,则取模 16384 后,得到 3,在 0-5460 之间,则放入对于的第一个节点。其他以此类推。
  • 跳转
    大家都知道,主从模式中,只有主节点可以写入数据,而从节点只能读取数据。在 Cluster 集群中,设置值时,如果计算出的槽位在另一台服务器上,则集群连接会自动跳转至相应服务器。
  • 网络抖动
    网络抖动,表示网络很不稳定。当出现这样的情况,可能一小段时间连接不上,可能就认为了节点挂了。这里就涉及到连接到超时时间,在网络不稳定的情况下,可以将超时时间设置长一些。
特点
  • 节点互通:所有的 Redis 节点彼此互联(PING-PONG机制),内部使用二进制协议优化传输速度和带宽;
  • 去中心化:Redis Cluster 不存在中心节点,每个节点都记录有集群的状态信息,并且通过 Gossip 协议,使每个节点记录的信息实现最终一致性;
  • 客户端直连:客户端与 Redis 节点直连,不需要中间 Proxy 层,客户端不需要连接集群所有节点,连接集群中任何一个可用节点即可;
  • 数据分片:Redis Cluster 的键空间被分割为 16384 个 Slot,这些 Slot 被分别指派给主节点,当存储 Key-Value 时,根据 CRC16(key) Mod 16384的值,决定将一个 Key-Value 放到哪个 Slot 中;
  • 多数派原则:对于集群中的任何一个节点,需要超过半数的节点检测到它失效(pFail),才会将其判定为失效(Fail);
  • 自动 Failover:当集群中某个主节点故障后(Fail),其它主节点会从故障主节点的从节点中选举一个“最佳”从节点升主,替代故障的主节点;
  • 功能弱化:集群模式下,由于数据分布在多个节点,不支持单机模式下的集合操作,也不支持多数据库功能,集群只能使用默认的0号数据库;
  • 集群规模:官方推荐的最大节点数量为 1000 个左右,这是因为当集群规模过大时,Gossip 协议的效率会显著下降,通信成本剧增。
分片

Redis 集群实现的基础是分片,即将数据集有机的分割为多个片,并将这些分片指派给多个 Redis 实例,每个实例只保存总数据集的一个子集。利用多台计算机内存和来支持更大的数据库,而避免受限于单机的内存容量;通过多核计算机集群,可有效扩展计算能力;通过多台计算机和网络适配器,允许我们扩展网络带宽。
基于“分片”的思想,Redis 提出了 Hash Slot。Redis Cluster 把所有的物理节点映射到预先分好的16384个 Slot 上,当需要在 Redis 集群中放置一个 Key-Value 时,根据 CRC16(key) Mod 16384的值,决定将一个 Key 放到哪个 Slot 中。
redis — 集群模式与原理介绍(一)_第4张图片

请求路由方式

客户端直连 Redis 服务,进行读写操作时,Key 对应的 Slot 可能并不在当前直连的节点上,经过“重定向”才能转发到正确的节点。客户端进行 Set 操作,当 Key 对应的 Slot 不在当前节点时,客户端会报错并返回正确节点的 IP 和端口。Set 成功则返回 OK。

以集群模式登录 127.0.0.1:6379 客户端(注意命令的差别:-c 表示集群模式),则可以清楚的看到“重定向”的信息,并且客户端也发生了切换:“6379” -> “6381”。
redis — 集群模式与原理介绍(一)_第5张图片
Redis Cluster 借助客户端实现的请求路由是一种混合形式的查询路由,它并非从一个 Redis 节点到另外一个 Redis,而是借助客户端转发到正确的节点。
实际应用中,可以在客户端缓存 Slot 与 Redis 节点的映射关系,当接收到 MOVED 响应时修改缓存中的映射关系。如此,基于保存的映射关系,请求时会直接发送到正确的节点上,从而减少一次交互,提升效率。

节点通信原理:Gossip 算法

在分布式系统中,需要提供维护节点元数据信息的机制,所谓元数据是指节点负责哪些数据、主从属性、是否出现故障等状态信息。常见的元数据维护方式分为集中式和无中心式。Redis Cluster 采用 Gossip 协议实现了无中心式。
Redis Cluster 中的每个 Redis 实例监听两个 TCP 端口,6379(默认)用于服务客户端查询,16379(默认服务端口+10000)用于集群内部通信。集群中节点通信方式如下:

  • 每个节点在固定周期内通过特定规则选择几个节点发送 Ping 消息;
  • 接收到 Ping 消息的节点用 Pong 消息作为响应。
1.节点间是如何交换信息的

Redis 节点启动之后,会每间隔 100ms 执行一次集群的周期性函数 clusterCron()。
一、当前节点向另一个节点发送 Ping 消息时,携带的其它节点的消息数量至少为3,最大等于集群节点总数-2;
二、为 Ping 消息体中选择携带的其它节点的信息时,采用的是混合选择模式:随机选择+偏好性选择,这样不仅可以保证 Gossip 协议随机传播的原则,还可以尽量将当前节点掌握的其它节点的故障信息传播出去。

2.如何保证消息传播的效率

前面已经提到,集群的周期性函数 clusterCron() 执行周期是 100ms,为了保证传播效率,每10个周期,也就是 1s,每个节点都会随机选择5个其它节点,并从中选择一个最久没有通信的节点发送 ing消息。
当然,这样还是没法保证效率,毕竟5个节点是随机选出来的,其中最久没有通信的节点不一定是全局“最久”。因此,对哪些长时间没有“被” 随机到的节点进行特殊照顾:每个周期(100ms)内扫描一次本地节点列表,如果发现节点最近一次接受 Pong 消息的时间大于 cluster_node_timeout/2,则立刻发送 Ping 消息,防止该节点信息太长时间未更新。
cluster_node_timeout 参数对消息发送的节点数量影响非常大。当带宽资源紧张时,可以适当调大这个参数,如从默认15秒改为30秒来降低带宽占用率。但是,过度调大 cluster_node_timeout 会影响消息交换的频率从而影响故障转移、槽信息更新、新节点发现的速度,因此需要根据业务容忍度和资源消耗进行平衡。同时整个集群消息总交换量也跟节点数成正比。
每个 Ping 消息的数据量体现在消息头和消息体中,其中消息头空间占用相对固定。消息体会携带一定数量的其它节点信息用于信息交换,消息体携带数据量跟集群的节点数息息相关,更大的集群每次消息通信的成本也就更高,因此对于 Redis 集群来说并不是越大越好。

故障转移

Redis Cluster 的故障转移可划分为三大步骤:故障检测、从节点选举以及故障倒换,以下详细介绍。

  • 故障检测

  • 单点视角检测
    集群中的每个节点都会定期通过集群内部通信总线向集群中的其它节点发送 Ping 消息,用于检测对方是否在线。如果接收 Ping 消息的节点没有在规定的时间内向发送 Ping 消息的节点返回 Pong 消息,那么,发送 Ping 消息的节点就会将接收 Ping 消息的节点标注为疑似下线状态(Probable Fail,Pfail)。

  • 检测信息传播
    集群中的各个节点会通过相互发送消息的方式来交换自己掌握的集群中各个节点的状态信息,如在线、疑似下线(Pfail)、下线(Fail)。例如,当一个主节点 A 通过消息得知主节点 B 认为主节点 C 疑似下线时,主节点 A 会更新自己保存的集群状态信息,将从 B 获得的下线报告保存起来。

  • 基于检测信息作下线判决
    如果在一个集群里,超过半数的持有 Slot(槽)的主节点都将某个主节点 X 报告为疑似下线,那么,主节点 X 将被标记为下线(Fail),并广播出去,所有收到这条 Fail 消息的节点都会立即将主节点 X 标记为 Fail。至此,故障检测完成。

  • 选举
    主节点被标记为 Fail 后,对应的从节点会发起投票,竞争升主。

  • 从节点拉票
    当从节点发现自己复制的主节点状态为已下线时,从节点就会向集群广播一条请求消息,请求所有收到这条消息并且具有投票权的主节点给自己投票。

  • 拉票优先级
    从节点在发现其主节点下线时,并非立即发起故障转移流程而进行“拉票”的,而是要等待一段时间,在未来的某个时间点才发起选举。
    mstime() + 500ms + random()%500ms + rank*1000ms
    rank即排名,排名是指当前从节点在下线主节点的所有从节点中的排名,排名主要是根据复制数据量来定,复制数据量越多,排名越靠前,因此,具有较多复制数据量的从节点可以更早发起故障转移流程,从而更可能成为新的主节点。

  • 主节点投票
    如果一个主节点具有投票权(负责处理 Slot 的主节点),并且这个主节点尚未投票给其它从节点,那么这个主节点将向请求投票的从节点返回一条回应消息,表示支持该从节点升主。

  • 根据投票结果决策
    在一个具有 N 个主节点投票的集群中,理论上每个参与拉票的从节点都可以收到一定数量的主节点投票,但是,在同一轮选举中,只可能有一个从节点收到的票数大于 N/2 + 1,也只有这个从节点可以升级为主节点,并代替已下线的主节点继续工作。

  • 选举失败
    没有一个候选从节点获得超过半数的主节点投票。遇到这种情况,集群将会进入下一轮选举,直到选出新的主节点为止。

  • 选举算法
    选举新主节点的算法是基于 Raft 算法的 Leader Election 方法来实现的。

  • 故障转移
    选举完成后,获胜的从节点将发起故障转移(Failover),角色从 Slave 切换为 Master,并接管原来主节点的 Slots,详细过程如下。
    新的主节点会通过轮询所有 Slot,撤销所有对已下线主节点的 Slot 指派,消除影响,并且将这些 Slot 全部指派给自己。
    新的主节点会向集群中广播一条 Pong 消息,将自己升主的信息通知到集群中所有节点。
    新的主节点开始处理自己所负责 Slot 对应的请求,至此,故障转移完成。

你可能感兴趣的:(redis,java)