简析Redis集群

Author Avatar
GeniusFunny 11月 27, 2018
  • 在其它设备中阅读本文章

Redis Cluster是Redis的分布式解决方案,有效地解决了Redis分布式方面的需求。当遇到单机内存、并发、流量等瓶颈时,可以采用Cluster的架构来达到负载均衡的目的。

数据分布

分布式数据库首先要解决把整个数据集按照分区规则映射到多个节点的问题,即将数据姐划分到多个节点上,每个节点只负责整体数据的一个子集。常见的分区规则有顺序分区和哈希分区两种,Redis采用的哈希分区规则。

下面是常见的几种哈希分区规则

节点取余分区

使用特定的数据key,再根据节点的数量N使用公式:hash(key)%N计算出哈希值,用来决定数据 映射到哪个节点上。这样的方式比较简单,常用于数据库的分库分表规则,一般采用预分区的的方式,提前根据数据量规划好分区数。但是缺点也比较明显,扩容和收缩时,数据节点的映射关系需要重新计算,会导致数据的重写迁移。

一致性哈希分区

系统为每个节点分配一个token,范围一般在0~2^32,这些token构成一个哈希环。数据读写执行时,先根据key计算hash值,然后顺时针找到第一个不小于该哈希值的token节点。相对于节点取余的方式,加入或删除节点只影响哈喜欢中相邻的节点,对其他节点无影响。但是,一致性哈希也存在一些问题:加减节点会造成哈希环中部分数据无法命中;当使用少量节点时,节点变化将大范围影响哈希环中数据映射;普通的一致性哈希分区在增减节点时需要增加一倍或删去一杯节点才能保证数据和负载的均衡。白话解析—一致性哈希算法

虚拟槽分区

虚拟槽分区使用分散度良好的哈希函数把所有的数据映射到一个固定范围的整数集合中,整数定义为槽(slot)。Redis Cluster的槽范围为0~16383,远远大于实际中的数据节点数。slot是集群内数据管理和迁移的基本单位。

Redis数据分区

Redis Cluster采用虚拟槽分区,所有的键根据哈希函数映射到0~16383整数槽内。每一个结点复制维护一部分槽以及槽所映射的键值数据。这样做的好处是:解耦数据与节点之间的关系;节点自身维护槽的映射关系,不需要客户端或代理服务维护槽分区元数据;支持节点、槽、键之间的映射查询,用于数据路由、在线伸缩等场景。

搭建集群

搭建集群分为准备节点、节点握手、分配槽等三个步骤。(可以用redis-trib.rb搭建集群,也可以根据Redis手动建立一个集群)

准备节点

节点握手

节点握手是指一批运行在集群模式下的节点通过Gossip协议彼此通信,达到感知对方的过程。

分配槽

只有把16384个槽全部分配给节点后,集群才进入在线状态。Redis Cluster会把所有数据映射到这16384个槽中,每个key会映射到一个固定的槽中,只有当节点分配了槽,才能响应和这些槽关联的键命令。

节点通信

在分布式存储中需要提供维护节点元数据(节点负责哪些数据,是否出现故障等状态信息)信息的机制。Redis Cluster采用用于p2p的Gossip协议,Gossip协议的工作原理就是节点不断通信交换信息,一段时间后所有节点都会知道集群完整的信息,类似流行病传播。

通信过程

  1. 集群中每个节点都会单独开辟一个TCP通道,用于节点之间彼此通信,通信端口号在基础端口上加10000。
  2. 每个节点在固定周期内通过特定规则选择几个节点发送ping消息。
  3. 接到ping消息的节点用pong消息作为响应。

Gossip消息

Gossip协议的主要职责就是信息交换,信息交换的载体就是节点彼此发送的Gossip消息。常用的Gossip消息分为:ping消息、pong消息、meet消息、fail消息。所有的消息格式划分为消息头和消息体,消息头包含发送节点自身状态数据,接受节点根据消息头就可以获取到发送节点的相关数据。

meet消息:用于通知新节点加入。

ping消息:集群内交换最频繁的消息,集群内每个节点每秒向多个其他节点发送ping消息,用于检测节点是否在线和交换彼此状态消息。

pong消息:当接收到ping、meet消息时,作为响应消息回复给发送方 确认消息正常通信。pong消息内部风中了自身状态数据。

fail消息:当节点判断集群内另一个结点下线时,会向集群内广播一个fail消息,其他节点接收到fail消息之后把对应节点更新为下线状态。

节点选择

Redis Cluster内节点通信采用固定频率(定时任务每秒执行10次)。因此节点每次选择需要通信的节点列表变得非常重要。通信节点选择过多虽然可以做到信息及时交换但成本过高,节点选择过少会降低集群内所有节点彼此信息交换频率,从而影响故障判断、新节点发现等需求的速度。

集群伸缩

Redis Cluster提供了灵活的节点扩容和收缩方案,在不影响对外服务的同时,为集群添加节点进行扩容或下线部分节点进行缩容,其中原理可抽象为槽和对应数据在不同节点之间灵活移动。

扩容集群

扩容集群的过程为准备新节点、新节点加入集群、迁移槽和数据等三个步骤。

迁移槽和数据

槽在迁移过程中,集群可以正常提供读写服务。槽是Redis Cluster数据管理和迁移的最小单位,所以首先需要为新节点制定槽迁移计划。槽迁移计划需要确保每个节点分配的槽数量接近,从而保证各节点的数据均匀。

数据迁移过程是逐个槽进行的,大体流程为:(1)目标节点准备导入槽;(2)源节点准备导出槽;(3)获取槽下{count}个键;(4)批量迁移相关键的数据;(5)循环迁移键,重复第3、4步;(6)通知槽分配给目标节点。

收缩集群

收缩集群以为需要下线部分节点,整个过程可分为下线迁移槽、忘记节点等两个步骤。

下线迁移槽

与扩容集群中的迁移槽和数据相似~

忘记节点

由于集群内的节点不断通过Gossip消息彼此交换节点状态,所以需要让其他节点不再与要下线的节点进行Gossip消息交换。

请求路由

为了追求性能最大化,Redis Cluster采用的是客户端直连节点的方式。

请求重定向

在集群模式下,Redis接受任何key相关的命令时首先计算对应的槽,再根据槽找出对应的节点,如果节点是自身,则处理命令;否则返回MOVED重定向错误,通知客户端请求正确的节点。节点对于不属于它的键命令只回复重定向响应,并不负责转发。键命令执行步骤主要分为两步:计算槽、查找槽所对应的节点。

计算槽

通过CRC16计算key的有效部分的hash值,再对16383取余,使每个键都能映射到0~16383槽范围内。

槽节点查询

Redis计算得到key对应的槽后,需要查询对应的节点。集群内通过消息交换每个节点都会知道所有节点的槽信息,内部保存在clusterState结构中。

Smart客户端

Smart客户端在内部维护者从slot->node的映射关系,本地就可以实现键到节点的查询,从而保证IO效率最大化,而MOVED重定向负责协助Smart客户端更新slot->node映射。

ASK重定向

Redis集群支持在线迁移槽和数据来完成水平伸缩。ASK重定向说明集群正在进行slot数据迁移,客户端无法知道什么时候迁移完成,因此只能是临时性的重定向,客户端不会更新slots缓存。但是MOVED重定向说明键对应的槽已经明确指定到新的节点,因此需要更新slots缓存。

故障转移

Redis Cluster实现了高可用,当集群内少量节点出现故障时通过自动故障转移保证集群可以正常的对外提供服务。

故障发现

Redis集群内节点通过ping/pong消息实现节点通信,消息不但可以传播节点槽信息,还可以传播主从状态、节点故障等信息,所以故障发现也是通过消息传播机制实现的。

主观下线

指某个节点认为另一个节点不可用,即下线状态。

客观下线

指标记一个节点真正的下线,集群内多个节点都认为该节点不可用,从而达成共识的结果。

故障恢复

故障节点变为客观下线后,如果下线节点是持有槽的主节点,则需要从它的从节点中选出一个替换他,从而保证见的高可用。整个大致分为资格检查、准备选举时间、发起选举、选举投票、替换主节点。