Redis 叢集中的紀元(epoch)
紀元(epoch)
Redis Cluster 使用了類似於 Raft 演算法 term(任期)的概念稱為 epoch(紀元),用來給事件增加版本號。Redis 叢集中的紀元主要是兩種:currentEpoch 和 configEpoch。
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。
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 的負責節點為自己。