Redis Cluster Specification
简单的翻译了一下官方关于cluster的说明,算是做个笔记,前面比较好懂的几章就没翻译了,大约翻译了一半吧,顺便加入了自己的理解,redis中午站上那个翻译的实在不太好。
原文链接
Fault Tolerance
心跳和gossip协议(gossip挺有意思的,可以查下维基,用于无中心的分布式系统中关键的配置信息进行发布,比如有A,B,C,D四个节点,每隔一段时间,A会向其中一个节点,比如B,发送一个关于A所知道的其他节点信息(比如B,C节点的IP,随机选几个,并不是A节点所知的所有的节点信息,这样信息量太大而且没有必要),B,C,D也会进行一样的工作,这样一传十十传百,在可以接受的时间内,这些信息就可以传遍整个网络,所有节点都知道整个集群其他节点的信息,就像八卦消息的传播。)
Redis集群节点持续不断的进行ping包的发送和pong包的接收,这两种报文有相同的结构并且都携带着重要的配置信息,只有类型是不同的。我们把ping 和pong包称之为心跳包
通常pong包只会在接收ping包后触发发送。但在需要将新的配置信息尽快广播的情况下,节点会单独发送pong包给其他节点,不需要ping包的触发。
通常情况下,一个节点每秒钟会对几个随机节点发送ping包,一定时间内一个节点发送的ping包是恒定的,与节点数量无关。
但是,每个节点会保证在NODE-TIMEOUT的一半时间间隔内,对所有其他没有发出ping包或接受到pong的节点发出ping包(即对那些没有信息交换的节点发出一次ping包)当NODE_TIMEOUT达到,节点还会尝试重新建立TCP链接防止因TCP断开导致的不可达。
当节点总数很大而NODE-TIMEOUT 较小时,会导致大量的心跳包交换。比如,有100个节点,NODE_TIMEOUT为60秒,每个节点在30秒内会发出99个ping包,3.3秒一个,集群中每秒发出330个。有方法可以降低心跳包的数量,但是考虑到其负载的可以接受的范围内故继续使用。
心跳包的内容
心跳包保护一个所有其他包都相同的头部和一个特有的gossip信息块。
头部包括以下信息
- node id
- 发送节点的currentEpoch和configEpoch(后面会介绍)如果是slave,currentEpoch就是其master的configEpoch 。
- node flage,表明是slave还是master,以及其他信息。
- bitmap,发送节点负责的slots号
- tcp断开,用于集群的tcp端口就是与客户端通信的断开加10000
- 集群的状态(down or ok)
gossip块中包括的关于其他节点的信息
- node id
- ip和端口号
- node flage
Failure detection
Redis集群的故障检测永远识别那些不能被多数节点(majority)访问的master或slave,进而选出一个slave来代替master。当slave推选失败,集群将不能提供服务。如前所言,每个节点有一个表记录他知道其他节点的flag状态,在Failure detection中使用的的flag标志是PFAIL和FAIL,PFAL意为possible failure,是尚未确认的故障状态,PAIL指这个节点失效的信息已经被大多数master确认。
PFAIL flag
当节点A在 NODE-TIMEOUT 内一直无法连接上节点B时,节点A和其slave就会将B标记为PFAIL,在Redis集群中,节点不可达指 NODE-TIMEOUT 内发送的ping报文没有回复,所以 NODE-TIMEOUT 应该大于网络往返延迟。
FAIL flag
PFAIL只是本地节点的信息,并不能触发slave的推选。要确认一个节点失效,需要FAIL标志。
如前所言,每个节点会想其他节点发送几个随机的节点信息。每个节点最终会接到其他所有就节点的信息。
要使PAIL转化为FAIL,需要以下条件:
- 一些节点,比如A,将B标记为PFAIL
- A通过gossip将B的状态通知给多数master节点
- 多数节点发出了B为PFAIL的信息,并且这些信息在有效期内(FAIL_REPORT_VALIDITY_MULT× NODE_TIMEOUT)
当以上条件满足,节点A将
- 标记B为FAIL
- 向所有可达节点发出B的FAIL信息FAIL信息将强迫接收节点将B标记为FAIL,不管之前的标记如何。
在以下情况FAIL将会被清除
- 节点可达并由slave接替
- 节点可达并且是server,而且它不再为任何slot服务,此时它并不再参与集群的服务,等待重新配置加入集群
- 节点可达并且是master,但已经超过一定时间且没能有slave替代,它将会重新加入集群。
PFAIL->FAIL是一个弱的转化协议。
- 当收集到多数节点的PAIL信息时,要注意这些消息的接收并不是在同一时刻,所以我们要放弃那些超时的报告。
- FIAL消息不一定能传递给全部节点,由于网络故障的缘故。
但是Reids集群的故障检测可以保证,,对某个节点是否故障,最终所有节点能达成一致。
Configuration handling, propagation, and failovers
Cluster current epoch
类似raft中的term,Redis集群使用epoch作为逻辑时钟来标记信息的顺序,判断是否过期,currentEpoch是一个64位的正整数。当节点初始化时,将其设定为0,每当接收到其他节点的包,将其头部的currentEpoch 项与本地的currentEpoch 比较,若本地的currentEpoch 小,将其更新。
目前Epoch机制只用于slave promotion。
Slave election and promotion
slave选举由slave自荐和其他master节点投票完成,当失效节点的一个slave得知其master在FAIL状态时,开始竞选。
为了竞选master,slave需要满足以下条件
- slave的master在FAIL态
- master服务的slot数量不为零
- 为了保证slave数据的新鲜性(reasonable fresh),slave与master连接断开的时间长度不能大于一个给定值,由用户设定。
slave竞选的第一步是增加其currentEpoch,然后向所有master节点要求选票。
竞选报文以 FAILOVER_AUTH_REQUEST包广播至所有master,并最多于2×NODE_TIMEOUT的时间内等待投票返回。(这个时间段就是一次选举的时间长度)
当master收到竞选请求,他会回复 FAILOVER_AUTH_ACK为其投票,在NODE_TIMEOUT * 2时间段内不会投票。
对于currentEpoch过期的AUTH_ACK的回复,slave将之抛弃,保证之前的选举投票不会影响现在的选举。
获得多数选票的slave赢得这次选举,否则此次选举失败, NODE_TIMEOUT * 4 时间后触发新的选举。
Slave rank
当得知master FAIL时,slave会等待一段时间然后参加竞选,等待的时间由以下公式计算
DELAY = 500 milliseconds + random delay between 0 and 500 milliseconds
+SLAVE_RANK * 1000 milliseconds.
固定的延迟保证FAIL的信息能在选举前传播至整个集群,否则其他master可能认为该节点还没有失效从而拒绝投票。
随机延迟用于避免多个slave同时开始竞选导致竞选失败(与raft算法中选举类似)
对于数据更新的slave(数据更接近master,与master最后一次数据通信的时间更为靠后)其,rank值较小,使得这些节点有更大的可能竞选成功。
竞选成功后,该节点会广播其成功的消息。
Hash slots configuration propagation
对于Redis集群的节点,知道其他节点负责服务的slot号是非常重要的,所以每个节点有一个配置文件指出哪个slot由哪个节点负责,这个配置文件有一个configEpoch号,以区别那些过期的配置文件,以下是hash slot onfigurations这个配置文件更新的方式
1. Heartbeat messages
2. UPDATE messages,当Heartbeat的接收者发现发送来的配置信息已经过期,则发出UPDATE强迫发出者更新。
(后面是举例没什么特别的,不写了。)
How nodes rejoin the cluster
简单的说就是,当一个节点A失效后恢复,重新加入后,他原先负责的slot就由其他节点替代,A得知道这个配置信息后,A master node will change its configuration to replicate (be a slave of) the node that stole its last hash slot.(汉语不好表示直接上原文)
Replica migration
目的很简单,举个栗子,A有两个slave,B有一个slave,B挂了以后就没有slave了,为了可用性,把A的一个slave转化为B的。
Replica migration algorithm
算法效果:保证每个master至少有一个slave
关键词:good slave:不是FAIL态的slave。**sacting slave**slave数量最多的那个master的node id最小的good slave
触发算法:当某个master连一个good slave都没有
configEpoch conflicts resolution algorithm
由于网络延迟,某些节点失效的发生等原因(详细的与看原文),两个节点可能发出同样的configEpoch 值的,(新的 configEpoch 就是目前最大的configEpoch 加一,两个节点同时获得configEpoch 导致相同的值),解决的方法很简单,另外节点收到含有冲突的configEpoch 的配置消息时,比较两个节点的node id,id小的主动放弃,使用更大的configEpoch 。