【Redis】Redis叢集主從調整演算法

風塵_NULL發表於2020-03-03

一、問題出發

1)Redis 叢集為什麼要調整主從?

  在大叢集中,一個叢集上百個redis 例項,一個 IP 可能有上十個例項,如果這十幾個例項都是主例項,那麼這麼大的流量會導致叢集的抖動

  當承載10 多個主例項的節點當機,叢集的通訊,資料的複製,可想而知,有多恐怖

2)Redis 大叢集調整主從的窘境

    100 多例項,人工調整,得耗費多少時間?

    例項關係錯綜複雜,有些IP 之前例項有直接主從關係,直接 cluster failover 就能調整,但是如果節點之間沒直接主從關係呢?甚至沒有間接主從關係?

二、深入環境的提煉與抽象

1)有直接主從關係;比如A 節點與 C 節點

 圖(一)


2) 有間接主從關係;例如:A->B->C

圖(2)

3) 節點之間無關係

比如:A 節點與 C 節點; D 節點與 C 節點;這裡我們繼續說明一個概念,一個叢集中,如果IP團體之間沒有關係,我們叫孤島,如果整個叢集都有關係,那麼叢集也是一個孤島;圖中AD是一個孤島,BC是一個孤島,即這個叢集有兩個孤島


圖(3)

三、提煉需求

(1)將孤島中從例項最多的節點跟從例項最少的節點進行交換(可能要進過多次切換,比如圖(2)要切換兩次),直到平衡( 為什麼說是要孤島,而不是叢集呢,因為一個孤島與另一個孤島之間沒有直接和間接主從關係,無法 cluster failover 達到平衡 )

(2)分割孤島( 每個孤島可以按照第一步來達到平衡 )


四、核心難點解決

1)將每個IP 的按需要移動從例項的個數排列,然後首尾切換,得到平衡

例如:這裡slave_most:3 表示有三個例項要變為主庫; slave_most:0 表示沒有例項變為主庫, slave_most:-2 表示有 2 個例項要從庫,這裡需要兩個資料結構,單個 IP redis 抽象( RedisNode ),以及整個叢集 IP Redis 的抽象( RedisNodes


圖(4)


2) 求切換路徑

  在《深入環境的提煉與抽象》這裡,我們講了一個孤島中,存在直接或者間接的主從關係,那麼理想的切換是有直接關係的,然後再找有間接關係,這樣我們可以 抽象成金字塔 自頂向下,從左向右掃描, 且掃描數層數不能超過節點總數 -1 ; 例如圖 2 ,我們從 A 節點開始掃描,目標是 C 節點 (A 是金字塔的頂層 ) ,發現 A 節點上的從例項對應的主例項有節點 D( D 是金字塔的第二層) D 上的從例項對用的主例項有 C,A(C A 即金字塔的第底層 ); 那麼圖 2 也可表示成 :

圖(5)

這裡還可以最佳化,就是底層的A 是可以去掉,因為形成了迴環,用剪枝的方法給他去掉


3) 求孤島

孤島,一堆例項有直接和間接主從關係的節點的集合; 找出每個節點的直接關係圖,然後有交集的就合併,沒交集或者合併後與其他的集合沒交集,就是孤島 , 以圖 (2) 為例:


圖(6)

孤島這裡描述不太清楚,直接上程式碼吧

    def getAreaNodes(self) :
        #單個ip的關係集合
        tmp_sets=[]
        #有主從關係的IP的集合,包括級聯關係
        area_sets=[]
        for r_node in self.m_nodes.redis_nodes :
            ip_list=set()
            ip_list.add(r_node.node_name)
            for entry in r_node.entrys :
                if entry.is_master() :
                    continue
                
                ip_list.add(entry.master_ip)
            tmp_sets.append(ip_list)
        #我只需要遍歷單個ip關係集合-1次,就能找出區域數
        num_t = len(tmp_sets)-1
        #遍歷最佳化,如果一個孤島的節點數就是叢集數,那可以立即返回
        finish_flag=False
        for out_index in range(0,num_t) :
            #找到有關聯關係的集合,則此值為True
            find_flag=False
            #一旦tmp_sets[0] 與後面的tmp_sets[1],tmp_sets[2],...任何一個下標為in_index有交集,則合併集合,開始下一輪迴圈
            for in_index in range(1,len(tmp_sets)) :
                #有交集,則合併
                if len([elem for elem in tmp_sets[0] if elem in tmp_sets[in_index]]) > 0 :
                    tmp_sets[0]=tmp_sets[0] | tmp_sets[in_index]
                    if len(tmp_sets[0]) == len(self.m_nodes.redis_nodes) :
                        area_sets.append(tmp_sets[0])
                        finish_flag=True
                        break 
                    del tmp_sets[in_index]
                    find_flag=True
                    break
                 
            if finish_flag==True :
                break
            #與其他集合無交集,那肯定是孤島,這這就是我們所求的孤島
            if find_flag==False or out_index==num_t-1 :
                area_sets.append(tmp_sets[0])
                del tmp_sets[0] 
        for area_index in range(0,len(area_sets)):
            self.node_areas.area_append(RedisNodes())
        for r_node in self.m_nodes.redis_nodes :
            for area_index in range(0,len(area_sets)):
                if r_node.node_name in area_sets[area_index] :
                    self.node_areas.areas[area_index].node_append(r_node)
        return self.node_areas













來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/30221425/viewspace-2678371/,如需轉載,請註明出處,否則將追究法律責任。

相關文章