纪元(epoch)
Redis Cluster
使用了类似于 Raft 算法 term(任期)的概念称为 epoch(纪元),用来给事件增加版本号。Redis 集群中的纪元主要是两种:currentEpoch
和 configEpoch
。
1、currentEpoch
这是一个集群状态相关的概念,可以当做记录集群状态变更的递增版本号
。每个集群节点,都会通过 server.cluster->currentEpoch
记录当前的 currentEpoch
。
集群节点创建时,不管是 master 还是 slave,都置 currentEpoch
为 0。当前节点接收到来自其他节点的包时,如果发送者的 currentEpoch
(消息头部会包含发送者的 currentEpoch)大于当前节点的currentEpoch
,那么当前节点会更新 currentEpoch
为发送者的 currentEpoch
。因此,集群中所有节点的 currentEpoch
最终会达成一致,相当于对集群状态的认知达成了一致。
currentEpoch 作用
currentEpoch
作用在于,当集群的状态发生改变,某个节点为了执行一些动作需要寻求其他节点的同意时,就会增加 currentEpoch
的值。目前 currentEpoch
只用于 slave 的故障转移流程,这就跟哨兵中的sentinel.current_epoch
作用是一模一样的。当 slave A 发现其所属的 master 下线时,就会试图发起故障转移流程。首先就是增加 currentEpoch
的值,这个增加后的 currentEpoch
是所有集群节点中最大的。然后slave A 向所有节点发起拉票请求,请求其他 master 投票给自己,使自己能成为新的 master。其他节点收到包后,发现发送者的 currentEpoch
比自己的 currentEpoch
大,就会更新自己的 currentEpoch
,并在尚未投票的情况下,投票给 slave A,表示同意使其成为新的 master。
2、configEpoch
这是一个集群节点配置相关的概念,每个集群节点都有自己独一无二的 configepoch
。所谓的节点配置,实际上是指节点所负责的槽位信息。
每一个 master 在向其他节点发送包时,都会附带其 configEpoch
信息,以及一份表示它所负责的 slots 信息。而 slave 向其他节点发送包时,其包中的 configEpoch 和负责槽位信息,是其 master 的 configEpoch
和负责的 slot 信息。节点收到包之后,就会根据包中的 configEpoch
和负责的 slots 信息,记录到相应节点属性中。
configEpoch 作用
configEpoch
主要用于解决不同的节点的配置发生冲突的情况。举个例子就明白了:节点A 宣称负责 slot 1,其向外发送的包中,包含了自己的 configEpoch
和负责的 slots 信息。节点 C 收到 A 发来的包后,发现自己当前没有记录 slot 1 的负责节点(也就是 server.cluster->slots[1]
为 NULL),就会将 A 置为 slot 1 的负责节点(server.cluster->slots[1]
= A),并记录节点 A 的 configEpoch
。后来,节点 C 又收到了 B 发来的包,它也宣称负责 slot 1,此时,如何判断 slot 1 到底由谁负责呢?
这就是 configEpoch
起作用的时候了,C 在 B 发来的包中,发现它的 configEpoch
,要比 A 的大,说明 B 是更新的配置。因此,就将 slot 1 的负责节点设置为 B(server.cluster->slots[1]
= B)。在 slave 发起选举,获得足够多的选票之后,成功当选时,也就是 slave 试图替代其已经下线的旧 master,成为新的 master 时,会增加它自己的 configEpoch
,使其成为当前所有集群节点的 configEpoch
中的最大值。这样,该 slave 成为 master 后,就会向所有节点发送广播包,强制其他节点更新相关 slots 的负责节点为自己。