Redis集群--水平扩展redis的读写性能

当服务出现性能瓶颈时,我们一般有2种方式扩展服务的性能:
1、垂直方向,就是通过升级硬件的配置,如内存出现瓶颈了,就使用更大的内存,CPU出现瓶颈了使用更好的CPU,但是硬件的升级总是有上限的,而且更高的硬件配置,往往意味着更高的成本,因为随着硬件成本的提升,对性能的提升并不是线性的。
2、水平方向,就是通过将服务部署到更多的机器,让更多的机器提供服务,提升服务的整体性能。理论上水平扩展可以无限的扩展服务的性能,垂直扩展只能是一个临时的方案。
Redis原生提供了集群方案redis cluster,用于水平的扩展redis的性能。

数据切片

数据切片有很多种方式,在我的博文哈希与一致性哈希中已经有详细的描述,这里就不在赘述。
Redis采用的是带虚拟节点的一致性has,redis集群中每个节点负责一部分槽(slot),key经过crc16(key)%0xffff,得到虚拟槽值(slot),再根据slot与节点的映射关系,就可以将请求路由到正确的节点。Redis通过虚拟slot分区解决了如下的问题:
1、数据稳定性,避免节点的扩容与缩容需要大规模的迁移数据。
2、均衡性与节点异构性。通过合理的分配slot与节点的映射关系,实现数据均衡与访问均衡以及节点异构性。
3、提供了命令用于查询slot与节点的映射关系,方便客户端缓存slot与节点的映射关系,从而实现高效的数据路由。
4、Redis节点自身保存slot与节点的映射关系,不需要引入第三方组件,降低运维难度。

请求路由

当第一次运行redis客户端的时候,客户端连接任意一个节点,通过cluster slots命令获取到slot与节点映射关系且缓存。客户端发送命令前通过hash算法crc16(key)%0xffff计算出slot的值,然后根据slot与节点的映射关系,将请求路由到相应的节点。客户端通过缓存slot与节点的映射关系,高效的将命令路由到相应的节点,但可能会出现如下的两种情况:
1、redis集群可能已经迁移数据,但客户端保存的slot与节点的映射关系可能还未更新,那么就会将命令路由到错的节点,redis收到命令后,会发现key对应的slot不是自己负责的,会给客户端返回如下的moved重定向错误

moved slot_value ip:port

客户端收到重定向错误后,更新slot与节点的映射关系,并且将命令请求到正确的节点。
2、redis集群可能正在迁移slot对应的数据,那么slot负责的key将会路由到原slot节点,此时存在两种情况,key还没有迁移出节点,那么直接返回。如果key已经迁移那么就会返回ask错误,客户端紧接着会将请求发送到正确的节点,注意这种情况并不更新slot与节点的对应关系。由于redis的单线程特性,key要么迁移完成了,要么没迁移,所以不存在迁移中的情况。

节点通信

Redis cluster使用gossip协议作为集群间通信的协议。Redis常用的gossip消息类型包括meet、ping、pong与fail等消息。
1、meet消息,当有节点需要加人集群,就会发送meet信息,信息接收方会回复pong信息。节点加人集群后,就可以周期性的发送ping与pong消息交换信息了。
2、ping消息,集群中最常使用的消息,集群节点会周期性的发送ping消息,检测节点是否存活,交换节点信息与slot映射信息。
3、pong消息,也是集群中最常使用的消息,当节点收到meet消息与ping消息,就会回复pong消息。节点也可以广播pong消息,告知节点变更。
4、fail消息,当有节点被判定下线,判定的节点就会向其他节点广播fail消息,其他节点收到消息后,就会标记相应的节点为下线。

通信开销

Redis会每隔1秒随机选择5个节点取其中一个最久没有通信的节点,发送ping消息。但是这种方式发送ping消息,可能会导致部分节点长时间没有通信。为此redis还会每隔100毫秒扫描全部节点,找出那些超过cluster-node-timeout/2的时间没有收到pong应答的节点立即发送ping报文。节点收到ping报文后会回复pong报文。
ping与pong报文都携带了如下的信息,本节点的状态信息、10%其他节点状态信息与slot映射信息。随着集群规模的增大,ping pong占用的网络带宽会急剧的上升,需要控制集群的规模,Redis给出的官方的集群规模上限为1000。
如果你的集群规模过大,可以按照业务逻辑将集群划分为多个小集群,同时适当的调大cluster-node-timeout的值,减少网络通信的开销。
同时部署集群的时候,最好能将集群均匀的部署到更多的主机上,否则当台主机的网络占用可能会非常的可观。

故障转移

Redis cluster本身提供了一套机制用于保证高可用。当集群中有节点出现故障了,能够自动的发现且完成故障转移,以保证高可用。

故障发现

Redis通过ping与pong消息实现节点间的通信,通过ping pong消息不仅可以交换节点的slot映射信息,还可以交换节点的主从状态、节点故障等节点信息。故障发现就是通过ping pong通信实现的,包括主观下线与客观下线两个状态。
1、主观下线,集群中的任意节点会周期性的向其他节点发送ping消息,如果某个节点在cluster-node-timeout的超时时间内都没有收到对方的pong应答,那么就会标记相应节点为主观下线。
2、客观下线,当一个节点被标记为主观下线后,标记其为主观下线的节点会通过ping消息向其他节点发送主观下线报告。主观下线报告随着ping pong消息在集群内传播,最终整个集群都可以感知到主观下线报告。当集群中的大多数持有slot信息的主节点都标记其为主观下线,那么就会触发客观下线流程,向所有节点广播fail消息。
那为什么是主节点才负责故障发现的决策呢?这是因为在集群模式下,只有主节点才负责slot映射信息的维护、数据的读写,从节点只是同步主节点的数据与状态信息。
那为什么需要半数以上的主节点标记某个节点为主观下线,才将节点标记为客观下线呢?这主要是为了避免网络分区情况下,导致集群被划分为多个小集群,多个小集群都进行故障转移导致数据的不一致。
需要注意的是,节点标记某个节点为主观下线是有有效期的。它的有效期是2✖cluster-node-timeout。下线报告是通过ping pong消息在集群中传播的,ping pong的传播机制可能会导致部分节点收到的状态更新不及时。比如节点A在1分钟前将节点B标记为主观下线,但是稍后恢复,如果节点C此时标记节点B为主观下线,如果节点C现在还是持有节点A的下线报告,那么不能使用。这个机制可以进一步避免误判,但是如果cluster-node-timeout设置的过短,可能会导致所以主节点在获取到大多数节点的下线报告前,下线报告就都已经失效了。
我用如下的流程图总结客观下线的判断过程:
Redis集群--水平扩展redis的读写性能_第1张图片

故障恢复

故障节点客观下线后,如果故障节点是主节点,就要触发选主,从从节点中选择一个新的主库替换故障节点。这个选主的过程如下图所示:
Redis集群--水平扩展redis的读写性能_第2张图片
1、资格检查。每个从节点都要检查与主节点的断连时间,如果断连时间超过cluster-node-timeout✖cluster-slave-validity-factor,那么就没有资格成为主节点。cluster-slave-validity-factor的默认值为10。如果为0,那么所有从节点都可以成为主节点。
2、计算延迟发起选举时间。Redis为了尽可能保证复制进度越大的节点优先成为主节点,会让每个节点延迟发起选举,复制进度越大的从节点,延迟的时间越短,延迟递增间隔为1000ms。同时为了避免同优先级的从节点同时发起选举,瓜分选票,会添加一个500毫秒以内的随机时间。
3、发起选举。从节点的选举时间到了后,就会发起选举,选举流程如下:
Redis集群--水平扩展redis的读写性能_第3张图片
(1)更新配置纪元。同raft算法一样,每届主节点都有一个单调递增配置纪元(config_epoch),同时整个集群还维护一个单调递增的全局配置纪元,其值为所有主节点的最大配置纪元。所有的主从节点共享一个相同的配置纪元。
配置纪元有如下的作用:
A.集群的所有状态信息以最大的配置纪元为准,配置纪元越大说明数据越新。当节点收到更新的配置纪元,则更新自己的保存的状态信息。
B.集群中从节点的配置纪元以相应主节点的为准。
C.当集群中有重大事件发生,都会更新全局的配置纪元与本节点的配置纪元,如触发选举、新节点加入、产生新的主节点等。
(2)广播选举消息。向集群中的所有节点广播故障选举消息(FAILOVER_AUTH_REQUEST)。
4、投票。当集群中的主节点收到故障选举消息,如果此配置纪元没有投过选票,那么就会给发起故障选举的节点投上一张选票。如果没有节点在2✖cluster-node-timeout的超时时间内没有收到大多数的选票,就会立即发起下一轮的领导者选举。
5、替换主节点。当节点收到超过半数的主节点的选票,就会发起替换主节点操作:
A.取消从旧主节点复制,升级为主节点,接管旧主节点的slot映射信息
B.广播PONG消息,通知所有其他节点当前从节点升级为主节点,其他从节点从新的主节点复制。

故障转移耗时

1、只有cluster-node-timeout的超时时间内没有收到pong响应,才会将节点标记为主观下线。
2、Ping消息发送机制只能保证cluster-node-timeout/2时间内给所有节点发送ping消息。
3、选举通常比较短,一般在1秒以内
为此故障转移花费的时间在cluster-node-timeout+cluster-node-timeout/2+1000毫秒内。

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