Redis集群节点变化如何处理?

来源:互联网 发布:java订票系统 编辑:程序博客网 时间:2024/05/29 16:41

1、集群currentEpoch

Redis Cluster使用了类似于Raft算法“term”(任期)的概念,那么在redis Clusterterm称为epoch,用来给events增量版本号。当多个nodes提供了信息有冲突时,它可以作为node来知道哪个状态是最新的。currentEpoch为一个64位无签名数字。

  在集群node创建时,masterslave都会将各自的currentEpoch设置为0,每次从其他node接收到数据包时,如果发现发送者的epoch值比自己的大,那么当前node将自己的currentEpoch设置为发送者的epoch。由此,最终所有的nodes都会认同集群中最大的epoch值;当集群的状态变更,或者node为了执行某个行为需求agreement时,都将需要epoch(传递或者比较)。

  当前来说,只有在slave提升期间发生;currentEpoch为集群的逻辑时钟(logical clock),指使持有较大值的获胜。(currentEpoch,当前集群已达成认同的epoch值,通常所有的nodes应该一样)

2configEpoch

  每个master总会在pingpong数据包中携带自己的configEpoch以及它持有的slots列表。新创建的node,其configEpoch0slaves通过递增它们的configEpoch来替代失效的master,并尝试获得其他大多数master的授权(认同)。当slave被授权,一个新的configEpoch被生成,slave提升为master且使用此configEpoch

  接下来介绍configEpoch帮助解决冲突,当不同的nodes宣称有分歧的配置时。

slavespingpong数据包中也会携带自己的configEpoch信息,不过这个epoch为它与master在最近一次数据交换时,masterconfigEpoch

  每当节点发现configEpoch值变更时,都会将新值写入nodes.conf文件,当然currentEpoch也也是如此。这两个变量在写入文件后会伴随磁盘的fsync,持久写入。严格来说,集群中所有的master都持有唯一的configEpoch值。同一组master-slaves持有相同的configEpoch

3slave选举与提升

  在slaves节点中进行选举,在其他masters的帮助下进行投票,选举出一个slave并提升为master。当master处于FAIL状态时,将会触发slave的选举。slaves都希望将自己提升为master,此master的所有slaves都可以开启选举,不过最终只有一个slave获胜。当如下情况满足时,slave将会开始选举:

a.当此slavemaster处于FAIL状态

b.master持有非零个slots

c.slavereplication链接与master断开时间没有超过设定值,为了确保此被提升的slave的数据是新鲜的,这个时间用户可以配置。

  为了选举,第一步,就是slave自增它的currentEpoch值,然后向其他masters请求投票(需求支持,votes)。slave通过向其他masters传播“FAILOVER_AUTH_REQUEST”数据包,然后最长等待2倍的NODE_TIMEOUT时间,来接收反馈。一旦一个master向此slave投票,将会响应“FAILOVER_AUTH_ACK”,此后在2 * NODE_TIMOUT时间内,它将不会向同一个masterslaves投票;虽然这对保证安全上没有必要,但是对避免多个slaves同时选举时有帮助的。slave将会丢弃那些epoch值小于自己的currentEpochAUTH_ACK反馈,即不会对上一次选举的投票计数(只对当前轮次的投票计数)。一旦此slave获取了大多数masterACKs,它将在此次选举中获胜;否则如果大多数master不可达(在2 * NODE_TIMEOUT)或者投票额不足,那么它的选举将会被中断,那么其他的slave将会继续尝试。

4slave rank(次序)

  当master处于FAIL状态时,slave将会随机等待一段时间,然后才尝试选举,等待的时间:DELAY = 500ms + random(0 ~ 500ms) + SLAVE_RANK * 1000ms一定的延迟确保我们等待FAIL状态在集群中传播,否则slave立即尝试选举(不进行等待的话),不过此时其他masters或许尚未意识到FAIL状态,可能会拒绝投票。延迟的时间是随机的,这用来去同步desynchronize),避免slaves同时开始选举。SLAVE_RANK表示此slave已经从master复制数据的总量的rank。当master失效时,slaves之间交换消息以尽可能的构建rank,持有replication offset最新的rank0,第二最新的为1,依次轮推。这种方式下,持有最新数据的slave将会首先发起选举(理论上)。当然rank顺序也不是严格执行的,如果一个持有较小rankslave选举失败,其他slaves将会稍后继续。

  一旦,slave选举成功,它将获取一个新的、唯一的、自增的configEpoch值,此值比集群中任何masters持有的都要大,它开始宣称自己是master,并通过pingpong数据包传播,并提供自己的新的configEpoch以及持有的slots列表。为了加快其他nodes的重新配置,pong数据包将会在集群中广播。当前node不可达的那些节点,它们可以从其他节点的ping或者pong中获知信息(gossip),并重新配置。

  其他节点也会检测到这个新的master和旧master持有相同的slots,且持有更高的configEpoch,此时也会更新自己的配置(epoch,以及master);旧masterslaves不仅仅更新配置信息,也会重新配置并与新的master跟进(slave of)。

5Masters响应slave的投票请求

  当Master接收到slave“FAILOVER_AUTH_REQUEST”请求后,开始投票,不过需要满足如下条件:

a.master只会对指定的epoch投票一次,并且拒绝对旧的epoch投票:每个master都持有一个lastVoteEpoch,将会拒绝AUTH_REQUESTcurrentEpochlastVoteEpoch小的请求。当master响应投票时,将会把lastVoteEpoch保存在磁盘中。

b.slavemaster处于FAIL状态时,master才会投票。

c.如果slavecurrentEpoch比此mastercurrentEpoch小,那么AUTH_REQUEST将会被忽略。因为master只会响应那些与自己的currentEpoch相等的请求。如果同一个slave再此请求投票,持有已经增加的currentEpoch,它(slave)将保证旧的投票响应不能参与计票。比如mastercurrentEpoch5lastVoteEpoch1

·slavecurrentEpoch3

·slave在选举开始时,使用epoch4(先自增),因为小于masterepoch,所以投票响应被延缓。

·slave在一段时间后将重新选举,使用epoch54 + 1,再次自增),此时master上延缓的响应发给slave,接收后视为有效。

·master2 * NODE_TIMEOUT超时之前,不会对同一个masterslave再次投票。这并不是严格需要,因为也不太可能两个slave在相同的epoch下同时赢得选举。不过,它确保当一个slave选举成功后,它(slave)有一段缓冲时间来通知其他的slaves,避免另一个slave赢得了新的一轮的选择,避免不必要的二次failover

·master并不会尽力选举最合适的slave。当slavemaster处于FAIL状态,此master在当前任期(term)内并不投票,只是批准主动投票者(即master不发起选举,只批准别人的投票)。最合适的slave应该在其他slaves之前,首先发起选举。

·master拒绝一个slave投票,并不会发出一个否决响应,而是简单的忽略。

·slave发送的configEpoch是其master的,还包括其master持有的slotsmaster不会向持有相同slots、但configEpoch只较低的slave投票。

6Hash Slots配置传播

Redis Cluster中重要的一部分就是传播集群中哪些节点上持有的哪些hash slots信息;无论是启动一个新的集群,还是当master失效其slave提升后更新配置,这对它们都至关重要。有2种方式用于hash slot配置的传播:

a.heartbeat 消息:发送者的pingpong消息中,总是携带自己目前持有的slots信息,不管自己是master还是slave

b.UPDATE 消息:因为每个心跳消息中会包含发送者的configEpoch和其持有的slots,如果接收者发现发送者的信息已经stale(比如发送者的configEpoch值小于持有相同slotsmaster的值),它会向发送者反馈新的配置信息(UPDATE),强制stale节点更新它。

  当一个新的节点加入集群,其本地的hash slots映射表将初始为NULL,即每个hash slot都没有与任何节点绑定。

·Rule 1:如果此node本地视图中一个hash slot尚未分配(设置为NULL),并且有一个已知的node声明持有它,那么此node将会修改本地hash slot的映射表,将此slot与那个node关联。slavefailover操作、reshard操作都会导致hash slots映射的变更,新的配置信息将会通过心跳在集群中传播。

·Rule 2:如果此node的本地视图中一个hash slot已经分配,并且一个已知的node也声明持有它,且此nodeconfigEpoch比当前slot关联的masterconfigEpoch值更大,那么此node将会把slot重新绑定到新的node上。根据此规则,最终集群中所有的nodes都赞同那个持有声明持有slot、且configEpoch最大值的nodesslot的持有者。

7nodes如何重新加入集群

node A被告知slot 12现在有node B接管,假如这两个slots目前有A持有,且A只持有这两个slots,那么此后A将放弃这2slots,成为空的节点;此后A将会被重新配置,成为其他新masterslave。这个规则可能有些复杂,A离群一段时间后重新加入集群,此时A发现此前自己持有的slots已经被其他多个nodes接管,比如slot 1B接管,slot 2C接管。在重新配置时,最终此节点上的slots将会被清空,那个窃取自己最后一个slotnode,将成为它的新master。节点重新加入集群,通常发生在failover之后,旧的master(也可以为slave)离群,然后重新加入集群。

8Replica迁移

Redis Cluster实现了一个成为“Replica migration”的概念,用来提升集群的可用性。比如集群中每个master都有一个slave,当集群中有一个master或者slave失效时,而不是master与它的slave同时失效,集群仍然可以继续提供服务。

a.master A,有一个slave A1

b.master A失效,A1被提升为master

c.一段时间后,A1也失效了,那么此时集群中没有其他的slave可以接管服务,集群将不能继续服务。

  如果mastersslaves之间的映射关系是固定的(fixed),提高集群抗灾能力的唯一方式,就是给每个master增加更多的slaves,不过这种方式开支很大,需要更多的redis实例。

  解决这个问题的方案,我们可以将集群非对称,且在运行时可以动态调整master-slaves的布局(而不是固定master-slaves的映射),比如集群中有三个master ABC,它们对应的slaveA1B1C1C2,即C节点有2slaves“Replica迁移可以自动的重新配置slave,将其迁移到某个没有slavemaster下。

a.A失效,A1被提升为master

b.此时A1没有任何slave,但是C仍然有2slave,此时C2被迁移到A1下,成为A1slave

c.此后某刻,A1失效,那么C2将被提升为master。集群可以继续提供服务。

Replica迁移算法:

  迁移算法并没有使用“agree”形式,而是使用一种算法来避免大规模迁移,这个算法确保最终每个master至少有一个slave即可。起初,我们先定义哪个slave是良好的:一个良好的slave不能处于FAIL状态。触发时机为,任何一个slave检测到某个master没有一个良好slave时。参与迁移的slave必须为,持有最多slavesmaster的其中一个slave,且不处于FAIL状态,且持有最小的node ID

  比如有10masters都持有一个slave,有2masters各持有5slaves,那么迁移将会发生在持有5slavesmasters中,且node ID最小的slave node上。我们不再使用“agreement”,不过也有可能当集群的配置不够稳定时,有一种竞争情况的发生,即多个slaves都认为它们自己的ID最小;如果这种情况发生,结果就是可能多个slaves会迁移到同一个master下,不过这并没有什么害处,但是最坏的结果是导致原来的master迁出了所有的slaves,让自己变得单一。但是迁移算法(进程)会在迁移完毕之后重新判断,如果尚未平衡,那么将会重新迁移。

  最终,每个master最少持有一个slave;这个算法由用户配置的“cluster-migration-barrier”,此配置参数表示一个master至少保留多少个slaves,其他多余的slaves可以被迁出。此值通常为1,如果设置为2,表示一个master持有的slaves个数大于2时,多余的slaves才可以迁移到持有更少slavesmaster下。

configEpoch冲突解决算法:

  在slave failover期间,会生成新的configEpoch值,需要保证唯一性。不过有2种不同的event会导致configEpoch的创建是不安全的:仅仅自增本地的currentEpoch并希望它不会发生冲突。这两个事件有系统管理员触发:

a.CLUSTER FAILOVER:这个指令,就是人为的将某个slave提升为master,而不需要要求大多数masters的投票参与。

b.slots的迁移,用于平衡集群的数据分布(reshard);此时本地的configEpoch也会修改,因为性能的考虑,这个过程也不需要“agreement”。在手动reshard期间,当一个hash slotA迁移到Bresharding程序将强制B更新自己的配置信息、epoch值也修改为集群的最大值+ 1(除非BconfigEpoch已经是最大值),这种变更则不需要其他nodesagreement(注意与failover的原理不同)。通常每次resharding都会迁移多个slots,且有多个nodes参与,如果每个slots迁移都需要agreement,才能生成新的epoch,这种性能是很差的,也不可取。我们在首个slots迁移开始时,只会生成一个新的configEpoch,在迁移完毕后,将新的配置传播给集群即可,这种方式在生产环境中更加高效。

  因为上述两个情况,有可能(虽然概率极小)最终多个nodes产生了相同的configEpoch;比如管理员正在进行resharding,但是此时failover发生了...无论是failover还是resharding都是将currentEpoch自增,而且resharding不使用agreement形式(即其他nodes或许不知道,而且网络传播可能延迟),这就会发生epoch值的冲突问题。

  当持有不同slotsmasters持有相同的configEpoch,这并不会有什么问题。比较遗憾的是,人工干预或者resharding会以不同的方式修改了集群的配置,Cluster要求所有的slots都应该被nodes覆盖,所以在任何情况下,我们都希望所有的master都持有不同的configEpoch。避免冲突的算法,就是用来解决当2nodes持有相同的configEpoch

a.如果一个master节点发现其他master持有相同的configEpoch

b.并且此master逻辑上持有较小的node ID(字典顺序)

c.然后此master将自己的currentEpoch1,并作为自己新的configEpoch

  如果有多个nodes持有相同的congfigEpoch,那么除了持有最大ID的节点外,其他的nodes都将往前推进(+1,直到冲突解决),最终保证每个master都持有唯一的configEpochslaveconfigEpochmaster一样)。对于新创建的cluster也是同理,所有的nodes都初始为不同的configEpoch

9Node resets

  所有的nodes都可以进行软件级的reset(不需要重启、重新部署它们),reset为了重用集群(重新设定集群),必须需要将某个(些)节点重置后添加到其他集群。我们可以使用“CLUSTER RESET”指令:

a.CLUSTER RESET SOFT

b.CLUSTER RESET HARD

  指令必须直接发给需要reset的节点,如果没有指定reset类型,默认为SOFT

a.softhard:如果节点为slave,那么节点将会转换为master,并清空其持有的数据,成为一个空的master。如果此节点为master,且持有slots数据,那么reset操作将被中断。

b.softhard:其上持有的slots将会被释放

c.softhard:此节点上的nodes映射表将会被清除,此后此node将不会知道其他节点的存在与状态。

d.hardcurrentEpochconfigEpochlastVoteEpoch值将被重置为0

e.hard:此nodeID将会重新生成。

  持有数据的(slot映射不为空的)master不能被reset(除非现将此master上的slot手动迁移到其他nodes上,或者手动failover,将其切换成slave);在某些特定的场景下,在执行reset之前,或许需要执行FLUSHALL来清空原有的数据。

10、集群中移除节点

  我们已经知道,将node移除集群之前,首先将其上的slots迁移到其他nodes上(reshard),然后关闭它。不过这似乎还并未结束,因为其他nodes仍然记住了它的ID,仍然不会尝试与它建立连接。因此,当我们确定将节点移除集群时,可以使用“CLUSTER FORGET”指令:

a.将此nodenodes映射表中移除。

b.然后设定一个60秒的隔离时间,阻止持有相同IDnode再次加入集群。

  之所以2)规则,因为FORGET指令将会通过gossip协议传播给其他nodes,集群中所有的节点都收到消息是需要一定的时间延迟。

 

来源:http://www.javaseo.cn/article/76/

0 0
原创粉丝点击