Redis高并发可用-主从复制,集群

发布于:2024-04-30 ⋅ 阅读:(30) ⋅ 点赞:(0)

Redis高并发可用

1 复制

默认情况下,Redis都是主节点。每个从节点只能有一个主节点,而主节点可以同时具有多个从节点。复制的数据流是单向的,只能由主节点复制到从节点。

1.1 复制的拓扑结构

一主一从

主一从结构是最简单的复制拓扑结构,用于主节点出现宕机时从节点提供故障转移支持

在这里插入图片描述
当应用写命令并发量较高且需要持久化时,可以只在从节点上开启AOF,这样既保证数据安全性同时也避免了持久化对主节点的性能干扰。但是,当主节点关闭持久化功能时,如果主节点脱机要避免自动重启操作。因为主节点之前没有开启持久化功能自动重启后数据集为空,这时从节点如果继续复制主节点会导致从节点也被情况的情况,丧失持久化的意义。安全的做法是在从节点上执行slaveof no one断开与主节点的复制关系,再重启主节点从而避免这一问题

一主多从结构

一主多从结构(又称为星形拓扑结构)使得应用端可以利用多个从节点实现读写分离

在这里插入图片描述

树状主从结构

树状主从结构(又称为树状拓扑结构)使得从节点不但可以复制主节点数据,同时可以作为其他从节点的主节

点继续向下层复制。通过引入复制中间层,可以有效降低主节点负载和需要传送给从节点的数据量

2 Redis 主从复制原理

在这里插入图片描述

1)保存主节点信息

执行slaveof后从节点只保存主节点的地址信息便直接返回,这时建立复制流程还没有开始

2)建立主从socket连接

从节点内部通过每秒运行的定时任务维护复制相关逻辑,当定时任务发现存在新的主节点后,会尝试与该节点建立网络连接。

从节点会建立一个socket套接字,专门用于接受主节点发送的复制命令。从节点连接成功后打印日志。

如果从接待你无法建立连接,定时任务会无限重试知道连接成功或者执行slaveof no one取消复制。

3)发送ping命令

连接建立成功后从节点发送ping请求进行首次通信,ping请求主要目的:检测主从之间网络套接字是否可

用、检测主节点当前是否可接受处理命令。

从节点发送的ping命令成功返回,Redis打印日志,并继续后续复制流程:

4)权限验证

如果主节点设置了requirepass参数,则需要密码验证,从节点必须配置masterauth参数保证与主节点相同

的密码才能通过验证;如果验证失败复制将终止,从节点重新发起复制流程。

5)同步数据集

主从复制连接正常通信后,对于首次建立复制的场景,主节点会把持有的数据全部发送给从节点,这部分操作是耗时最长的步骤。Redis在2.8版本以后采用新复制命令 psync进行数据同步,原来的sync命令依然支持,保证新旧版本的兼容性。新版同步划分两种情况:全量同步和部分同步

6)命令持续复制

当主节点把当前的数据同步给从节点后,便完成了复制的建立流程。接下来主节点会持续地把写命令发送给

从节点,保证主从数据一致性。

3 Redis数据同步

Redis早期支持的复制功能只有全量复制(sync命令),它会把主节点全部数据一次性发送给从节点,当数

据量较大时,会对主从节点和网络造成很大的开销。

Redis在2.8版本以后采用新复制命令psync进行数据同步,原来的sync命令依然支持,保证新旧版本的兼容

性。新版同步划分两种情况:全量复制和部分复制。

3.1 全量同步

全量复制:一般用于初次复制场景。是第一次建立复制时必须经历的阶段。触发全量复制的命令是sync和psync

在这里插入图片描述

1):发送pysnc命令进行数据同步,由于第一次进行复制,从节点没有复制偏移量和主节点的运行id,所以发送pysnc?-1

2)主节点根据psync ? -1解析出当前为全量复制,回复 +FULLRESYNC响应。从节点接收主节点的响应数据保存运行ID和偏移量offset,并且打印日志。

3)主节点执行bgsave保存rdb文件到本地

4)主节点发送RDB文件给从节点,从节点把接收的RDB文件保存在本地并直接作为从节点的数据文件,接收

完RDB后从节点打印相关日志,可以在日志中查看主节点发送的数据量

5)对于从节点开始接收RDB快照到接收完成期间,主节点仍然响应读写命令,因此主节点会把这期间写命

令数据保存在复制客户端缓冲区内,当从节点加载完RDB文件后,主节点再把缓冲区内的数据发送给从节点,

保证主从之间数据一致性。

通过分析,全量复制的所有流程是一个非常耗时的过程,

他的时间开销主要包括:

  1. 主节点bgsave时间
  2. RDB文件网络传输时间
  3. 从节点清空数据时间
  4. 从节点加载RDB的时间
  5. 可能的AOF重写时间

最大的问题是复制可能会失败。

3.2 部分同步

使用用psync {runId} {offset} 命令实现

当从节点正在复制主节点时,如果网络闪断或者命令丢失等异常情况时,从节点会向主节点不发丢失的命令数据,如果主节点的复制积压缓冲器内存在这部分数据则直接发送给从节点。这样可以保持主从节点复制的一致性。

在这里插入图片描述

流程说明:

  1. 当主从节点之间网络出现中断时,如果repl-timeout时间,主节点会认为从节点故障并中断复制连接,打印日志。如果此时从节点没有宕机,也会打印与主节点连接丢失的日志。
  2. 主从连接中断期间主节点依然响应命令,但因复制连接中断命令无法发送给从节点,不过主节点内部存在的复制积压缓冲区,依然可以保存最近一段时间的写命令数据,默认最大缓存1MB。
  3. 当主从节点网络恢复后,从节点会再次连上主节点,打印日志。
  4. 当主从连接恢复后,由于从节点之前保存了自身已复制的偏移量和主节点的运行ID。因此会把它们当作psync参数发送给主节点,要求进行部分复制操作。
  5. 主节点接到psync命令后首先核对参数runId是否与自身一致,如果一致,说明之前复制的是当前主节点;之后根据参数offset在自身复制积压缓冲区查找,如果偏移量之后的数据存在缓冲区中,则对从节点发+CONTINUE响应,表示可以进行部分复制。如果不再,则退化为全量复制
  6. 主节点根据偏移量把复制积压缓冲区里的数据发送给从节点,保证主从复制进入正常状态。发送的数据量可以在主节点的日志,传递的数据远远小于全量数据。

心跳

主从节点在建立连接后,他们之间维护者长连接并彼此发送心跳命令

主从心跳判断机制:

  • 主从节点批次都有心跳检测机制,各自模仿对方的客户端进行通信,通过client list命令查看复制相关客户端信息,主节点的连接状态为flags=M,从节点连接状态为flags=S,
  • 主节点默认每隔10秒对从节点发送ping命令,判断从节点的存活性和连接状态。可通过repl-ping-slave-period控制发送频率。
  • 从节点在主线程每隔1秒发送replconf ack {offset}命令,给主节点上报自身当前的复制偏移量。

replconf命令的主要作用:

  1. 实时检测主从节点网络状态
  2. 上报自身复制偏移量,检查数据是否丢失,如果从节点数据丢失丢失,再从主节点的复制缓冲区拉取丢失数据。
  3. 实现保证从节点的数量和延迟性功能。
  4. 主节点根据replconf命令判断从节点超时时间,体现在info replication统计中的lag信息中,lag表示与从节点最后一次通信延迟的秒数,正常延迟应该在0和1之间。如果超过repl-timeout配置的值((默认60秒),则判定从节点下线并断开复制客户端连接。即使主节点判定从节点下线后,如果从节点重新恢复,心跳检测会继续进行。

4 哨兵Redis Sentinel

Redis的主从复制模式下,一旦主节点由于故障不能提供服务,需要人工将从节点晋升为主节点,同时还要通知应用方更新主节点地址,这是无法接受的。

Redis2.8提供了Redis Sentinel来解决这个问题

4.1 Redis Sentinel

Redis Sentinel是一个分布式架构,其中包含若干个Sentinel节点和Redis数据节点,每个Sentinel节点会对数据节点和其余Sentinel节点进行监控,当它发现节点不可达时,会对节点做下线标识。如果被标识的是主节点,它还会和其他Sentinel节点进行“协商”,当大多数Sentinel节点都认为主节点不可达时,它们会选举出一个Sentinel节点来完成自动故障转移的工作,同时会将这个变化实时通知给Redis应用方。整个过程完全是自动的,不需要人工来介入,所以这套方案很有效地解决了Redis的高可用问题。
在这里插入图片描述

4.2 实现原理

4.2.1 三个定时监控任务

如何判断节点不可达:通过三个定时健康任务完成

1:每隔10秒的定时监控

每个10秒,每个sentinel节点会向主节点和从节点发送Info命令获取最新的拓扑结构,sentinel节点通过对上述结果进行解析就可以找到相应的从节点。

作用具体表现在三个方面:

  • 通过主节点执行info命令,获取从节点的信息。这也是为什么Sentinel节点不需要显式配置监控从节点。
  • 当有新的节点加入时都可以立刻感知出来。
  • 节点不可达或者故障转移后,可以通过info实时更新节点的拓扑信息。

2:每隔2秒的定时监控

在这里插入图片描述

每隔2秒,每个sentinel节点会向数据节点的sentinel:hello频道上发送该sentinel节点对于主节点的判断以及当前sentinel1节点的信息。同时每个sentinel节点也会订阅该频道,来了解其他sentinel节点以及他们对主节点的判断。

这个定时任务完成以下工作:

  • 通过订阅主节点的sentinel:hello了解其他的Sentinel节点信息,如果是新加入的Sentinel节点,将该Sentinel节点信息保存起来,并与该 Sentinel节点创建连接
  • Sentinel节点之间交换主节点的状态,作为后面客观下线以及领导者选举的依据。

3:每隔一秒的定时监控

在这里插入图片描述

每隔1秒,每个Sentinel节点会向主节点、从节点、其余Sentinel节点发送一条ping命令做一次心跳检测,来

确认这些节点当前是否可达。

通过上面的定时任务,Sentinel节点对主节点、从节点、其余Sentinel节点都建立起连接,实现了对每个节点

的监控,这个定时任务是节点失败判定的重要依据。

4.2.2 主观下线

每个Sentinel节点会每隔1秒对主节点、从节点、其他Sentinel节点发送ping命令做心跳检测,当这些节点超过down-after-milliseconds没有进行有效回复,Sentinel节点就会对该节点做失败判定,这个行为叫做主观下线。从字面意思也可以很容易看出主观下线是当前Sentinel节点的一家之言,存在误判的可能。

4.2.3 客观下线

在这里插入图片描述

当Sentinel主观下线的节点是主节点时,该Sentinel节点会通过sentinel is-master-down-by-addr命令向其

他Sentinel节点询问对主节点的判断,当超过个数,Sentinel节点认为主节点确实有问题,这时该

Sentinel节点会做出客观下线的决定,这样客观下线的含义是比较明显了,也就是大部分Sentinel节点都对主

节点的下线做了同意的判定,那么这个判定就是客观的。

领导者sentinel节点选举

在这里插入图片描述

假如Sentinel节点对于主节点已经做了客观下线,那么是不是就可以立即进行故障转移了?当然不是,实际上故障转移的工作只需要一个Sentinel节点来完成即可,所以 Sentinel节点之间会做一个领导者选举的工作,选出一个Sentinel节点作为领导者进行故障转移的工作。Redis使用了Raft算法实现领导者选举,RedisSentinel进行领导者选举的大致思路如下:

1 )每个在线的Sentinel节点都有资格成为领导者,当它确认主节点主观下线时候,会向其他Sentinel节点发

送sentinel is-master-down-by-addr命令,要求将自己设置为领导者。

2)收到命令的Sentinel节点,如果没有同意过其他Sentinel节点的sentinel is-master-down-by-addr命令,将

同意该请求,否则拒绝。

3)如果该Sentinel节点发现自己的票数已经大于等于max (quorum,num(sentinels)/2+1),那么它将成为

领导者。

4)如果此过程没有选举出领导者,将进入下一次选举。

故障转移
在这里插入图片描述

1)在从节点列表中选出一个节点作为新的主节点,选择方法如上图所示:

2) Sentinel领导者节点会对第一步选出来的从节点执行slaveof no one命令让其成为主节点。

3) Sentinel领导者节点会向剩余的从节点发送命令,让它们成为新主节点的从节点,复制规则和parallel

syncs参数有关。

4)) Sentinel节点集合会将原来的主节点更新为从节点,并保持着对其关注,当其恢复后命令它去复制新的主

节点。

5 集群

5.1数据分布理论

分布式数据库首先要解决把整个数据集按照分区规则映射到多个节点的问题,即把数据集划分为到多个节点上,每隔节点负责整体数据的一个子集。

需要重点关注数据分区规则。常见的分区规则有哈希分区和顺序分区两种。哈希分区离散度好、数据分布业务无关、无法顺序访问,顺序分区离散度易倾斜、数据分布业务相关、可顺序访问

5.2 哈希分区

节点取余分区:使用特定的数据,如Redis的键或用户ID,再根据节点数量N使用公式,hash(key)%N计算出哈希值,用来决定数据映射到哪一个节点上。这种方案存在一个问题:当节点数量变化时,如扩容或收缩节点,数据节点映射关系需要重新计算,会导致数据的重新迁移。

一致性哈希分区:实现思路是为系统每个节点分配一个token,范围在0-23,这些token构成一个hash环,数据读写执行节点查找操作时,先根据key计算hash值,然后顺时针找到第一个大于等于该hash值的token节点。

例如:集群中有三个节点(Node1、Node2、Node3),五个键(key1、key2、key3、key4、key5),其路由规则为

在这里插入图片描述

假如在Node2 和Node3之间增加Node4.变成如下图:
在这里插入图片描述

这种方式相比节点取余最大的好处在于加入和删除节点只影响哈希环中相邻的节点,对其他节点无影响。但

一致性哈希分区存在几个问题:

  • 当使用少量节点时,节点变化将大范围影响哈希环中数据映射,因此这种方式不适合少量数据节点的分布式方案。
  • 增加节点只能对下一个邻近节点有比较好的负载分担效果,例如上图中增加了节点Node4只能够对Node3分担部分负载,对集群中其他的节点基本没有起到负载分担的效果;类似地,删除节点会导致下一个相邻节点负载增加,而其他节点却不能有效分担负载压力。

正因为这些问题哈,统采用虚拟槽对一致性哈希进行改进,比如虚拟一致性哈希分区

虚拟一致性哈希分区

在这里插入图片描述

为了在增删节点的时候,各节点能够保持动态的均衡,将每个真实节点虚拟出若干个虚拟节点,再将这些虚

拟节点随机映射到环上。此时每个真实节点不再映射到环上,真实节点只是用来存储键值对,它负责接应各

自的一组环上虚拟节点。当对键值对进行存取路由时,首先路由到虚拟节点上,再由虚拟节点找到真实的节

点。

虚拟槽分区

Redis则是利用了虚拟槽分区,它使用分散度良好的哈希函数把所有数据映射到一个固定范围的整数集合中,正数定义为槽(slot),这个范围一般远远大于节点数,比如RedisCluster槽范围是0 ~16383。槽是集群内数据管理和迁移的基本单位。采用大范围槽的主要目的是为了方便数据拆分和集群扩展。每个节点会负责一定数量的槽。

比如集群有3个节点,则每个节点平均大约负责5460个槽。由于采用高质量的哈希算法,每个槽所映射的数

据通常比较均匀,将数据平均划分到3个节点进行数据分区。Redis Cluster就是采用虚拟槽分区,下面就介绍

Redis 数据分区方法。

在这里插入图片描述

5.3 Redis数据分区

Redis Cluser采用虚拟槽分区,所有的键根据哈希函数映射到0 ~16383整数槽内,计算公式:slot=CRC16(key) &16383。每一个节点负责维护―部分槽以及槽所映射的键值数据。

Redis虚拟槽分区的特点:

1、解耦数据和节点之间的关系,简化了节点扩容和收缩难度。

2、节点自身维护槽的映射关系,不需要客户端或者代理服务维护槽分区元数据。口支持节点、槽、键之间

的映射查询,用于数据路由、在线伸缩等场景。

3、数据分区是分布式存储的核心,理解和灵活运用数据分区规则对于掌握Redis Cluster非常有帮助。

集群功能限制

1、 key批量操作支持有限。如mset、mget,目前只支持具有相同slot值的key执行批量操作。对于映射为不

同slot值的key由于执行mget、mget等操作可能存在于多个节点上因此不被支持。

2、key事务操作支持有限。同理只支持多key在同一节点上的事务操作,当多个key分布在不同的节点上时无

法使用事务功能。

3、key作为数据分区的最小粒度,因此不能将一个大的键值对象如hash、list等映射到不同的节点。

4、不支持多数据库空间。单机下的Redis可以支持16个数据库,集群模式下只能使用一个数据库空间,即 db0。

5、复制结构只支持一层,从节点只能复制主节点,不支持嵌套树状复制结构。

5.4 集群原理

5.4.1 节点通信

在分布式存储中需要提供维护节点元数据信息的机制,所谓元数据是指:节点负责哪些数据,是否出现故障等状

态信息。常见的元数据维护方式分为:集中式和P2P方式。Redis集群采用P2P的Gossip(流言)协议,Gossip

协议工作原理就是节点彼此不断通信交换信息,一段时间后所有的节点都会知道集群完整的信息,这种方式类似

流言传播。

通信过程说明:

1)集群中的每个节点都会单独开辟一个TCP通道,用于节点之间彼此通信,通信端口号在基础端口上加10000。

2)每个节点在固定周期内通过特定规则选择几个节点发送ping消息。

3)接收到ping消息的节点用pong消息作为响应。

集群中每个节点通过一定规则挑选要通信的节点,每个节点可能知道全部节点,也可能仅知道部分节点,只要

这些节点彼此可以正常通信,最终它们会达到一致的状态。当节点出故障、新节点加入、主从角色变化、槽

信息变更等事件发生时,通过不断的ping/pong消息通信,经过一段时间后所有的节点都会知道整个集群全

部节点的最新状态,从而达到集群状态同步的目的。

Gossip 消息

Gossip协议的主要职责就是信息交换。信息交换的载体就是节点彼此发送的Gossip消息,了解这些消息有助

于我们理解集群如何完成信息交换。

常用的Gossip消息可分为:ping消息、pong消息、meet消息、fail消息等

meeting消息:

用于通知新节点加入。消息发送者通知接收者加入到当前集群,meet消息通信正常完成后,接收节点会加入

到集群中并进行周期性的ping、pong消息交换。

ping消息

集群内最频繁的消息,集群内每个节点每秒向多个其他节点发送ping消息,用于检测节点是否在线和交换彼此状态信息。ping消息发送封装了自身节点和部分其他节点的状态数据。

pong消息:

当接收到ping、meet消息时,作为响应消息回复给发送方确认消息正常通信。pong消息内部封装了自身状态数据。节点也可以向集群内广播自身的pong消息来通知整个集群对自身状态进行更新。

fail消息

当节点判定集群内另一个节点下线时,会向集群内广播一个fail消息,其他节点接收到fail消息之后把对应节点

更新为下线状态。

5.4.2 节点选择

虽然Gossip协议的信息交换机制具有天然的分布式特性,但它是有成本的。由于内部需要频繁地进行节点信

息交换,而ping/pong消息会携带当前节点和部分其他节点的状态数据,势必会加重带宽和计算的负担。

Redis集群内节点通信采用固定频率(定时任务每秒执行10次)。

因此节点每次选择需要通信的节点列表变得非常重要。通信节点选择过多虽然可以做到信息及时交换但成本

过高。节点选择过少会降低集群内所有节点彼此信息交换频率,从而影响故障判定、新节点发现等需求的速

度。因此Redis集群的Gossip协议需要兼顾信息交换实时性和成本开销。

选择发送消息的节点数量:

集群内每个节点维护定时任务,默认间隔1秒,每秒执行10次,定时任务里每秒随机选取5个节点,找出最久没有通信的节点发送ping消息,用于保证 Gossip信息交换的随机性。同时每100毫秒都会扫描本地节点列表,如果发现节点最近一次接受pong消息的时间大于cluster_node_timeout/2,则立刻发送ping消息,防止该节点信息太长时间未更新。

根据以上规则得出每个节点每秒需要发送ping消息的数量= 1 +10

消息数据量:

[CLUSTER_SLOTS/8],占用2KB,这块空间占用相对固定。消息体会携带一定数量的其他节点信息用于信息

交换。

根消息体携带数据量跟集群的节点数息息相关,更大的集群每次消息通信的成本也就更高,因此对于Redis集

群来说并不是大而全的集群更好。

5.4.3 故障转移

主观下线:集群中每个节点会定期向其他节点发送ping消息,接收节点回复pong消息作为响应。如果在cluster-node-timeout时间内通信一直失败,则发送节点会认为接收节点存在故障,把接收节点标记为主观下线状态。

流程说明:

1)节点a发送ping消息给节点b,如果通信正常将接收到pong消息,节点 a更新最近一次与节点b的通信时

间。

2)如果节点 a与节点b通信出现问题则断开连接,下次会进行重连。如果一直通信失败,则节点a记录的与节

点b最后通信时间将无法更新。

3)节点a内的定时任务检测到与节点b最后通信时间超高cluster-node-timeout时,更新本地对节点b的状态

为主观下线(pfail)。

客观下线:

当某个节点判断另一个节点主观下线后,相应的节点状态会跟随消息在集群内传播。

ping/pong消息的消息体会携带集群1/10的其他节点状态数据,当接受节点发现消息体中含有主观下线的节点状态时,会在本地找到故障节点的ClusterNode结构,保存到下线报告链表中。

通过Gossip消息传播,集群内节点不断收集到故障节点的下线报告。当半数以上持有槽的主节点都标记某个

节点是主观下线时。触发客观下线流程。这里有两个问题:

1)为什么必须是负责槽的主节点参与故障发现决策?因为集群模式下只有处理槽的主节点才负责读写请求和集

群槽等关键信息维护,而从节点只进行主节点数据和状态信息的复制。

2)为什么半数以上处理槽的主节点?必须半数以上是为了应对网络分区等原因造成的集群分割情况,被分

割的小集群因为无法完成从主观下线到客观下线这一关键过程,从而防止小集群完成故障转移之后继续对外

提供服务。

5.4 故障恢复

故障节点变为客观下线后,如果下线节点是持有槽的主节点则需要在它的从节点中选出一个替换它,从而保证集

群的高可用。下线主节点的所有从节点承担故障恢复的义务,当从节点通过内部定时任务发现自身复制的主

节点进入客观下线时,将会触发故障恢复流程。

**资格审查:**每个从节点都要检查最后与主节点断线时间,判断是否有资格替换故障的主节点。如果从节点与主节点断线时间超过cluster-node-time * cluster-slave-validity-factor,则当前从节点不具备故障转移资格。参数cluster-slave-validity-factor用于从节点的有效因子,默认为10。

**准备选举时间:**当从节点符合故障转移资格后,更新触发故障选举的时间,只有到达该时间后才能执行后续流程。

这里之所以采用延迟触发机制,主要是通过对多个从节点使用不同的延迟选举时间来支持优先级问题。复制偏移量越大说明从节点延迟越低,那么它应该具有更高的优先级来替换故障主节点。

所有的从节点中复制偏移量最大的将提前触发故障选举流程。主节点b进入客观下线后,它的三个从节点根据自身复制偏移量设置延迟选举时间,如复制偏移量最大的节点slave b-1延迟1秒执行,保证复制延迟低的从节点优先发起选举。

发起选举:

1:更新配置纪元。配置纪元是一个只增不减的整数,每个主节点自身维护一个配置纪元(clusterNode.configEpoch)标示当前主节点的版本,所有主节点的配置纪元都不相等,从节点会复制主节点的配置纪元。整个集群又维护一个全局的配置纪元(clusterstate.currentEpoch),用于记录集群内所有主节点配置纪元的最大版本。

配置纪元的主作用:标示集群内每个主节点的不同版本和当前集群最大的版本。

选举投票:

只有持有槽的主节点才会处理故障选举消息(FAILOVER_AUTH_REQUEST),因为每个持有槽的节点在一个配

置纪元内都有唯一的一张选票,当接到第一个请求投票的从节点消息时回复FAILOVER_AUTH_ACK消息作为

投票,之后相同配置纪元内其他从节点的选举消息将忽略。

投票过程其实是一个领导者选举的过程,如集群内有N个持有槽的主节点代表有N张选票。由于在每个配置纪

元内持有槽的主节点只能投票给一个从节点,因此只能有一个从节点获得 N/2+1的选票,保证能够找出唯一的

从节点。

Redis集群没有直接使用从节点进行领导者选举,主要因为从节点数必须大于等于3个才能保证凑够N/2+1个

节点,将导致从节点资源浪费。使用集群内所有持有槽的主节点进行领导者选举,即使只有一个从节点也可以

完成选举过程。

当从节点收集到N/2+1个持有槽的主节点投票时,从节点可以执行替换主节点操作,例如集群内有5个持有槽

的主节点,主节点b故障后还有4个,当其中一个从节点收集到3张投票时代表获得了足够的选票可以进行替

换主节点操作,。

投票作废:每个配置纪元代表了一次选举周期,如果在开始投票之后的cluster-node-timeout*2时间内从节点没

有获取足够数量的投票,则本次选举作废。从节点对配置纪元自增并发起下一轮投票,直到选举成功为止。

替换主节点

当从节点收集到足够的选票之后,触发替换主节点操作:

1)当前从节点取消复制变为主节点。

2)执行clusterDelslot 操作撤销故障主节点负责的槽,并执行clusterAddSlot把这些槽委派给自己。

3)向集群广播自己的pong消息,通知集群内所有的节点当前从节点变为主节点并接管了故障主节点的槽信