從源碼分析 MGR 的新主選舉算法

MGR 的新主選舉算法,在節點版本一致的情況下,其實也挺簡單的 。
首先比較權重,權重越高,選為新主的優先級越高 。
如果權重一致 , 則會進一步比較節點的 server_uuid 。server_uuid 越小,選為新主的優先級越高 。
所以,在節點版本一致的情況下,會選擇權重最高 , server_uuid 最小的節點作為新的主節點 。
節點的權重由 group_replication_member_weight 決定,該參數是 MySQL 5.7.20 引入的 , 可設置 0 到 100 之間的任意整數值,默認是 50 。
但如果集群節點版本不一致,實際的選舉算法就沒這么簡單了 。
下面 , 我們結合源碼具體分析下 。
代碼實現邏輯新主選舉算法主要會涉及三個函數:

  1. pick_primary_member
  2. sort_and_get_lowest_version_member_position
  3. sort_members_for_election
這三個函數都是在 primary_election_invocation_handler.cc 中定義的 。
其中,pick_primary_member 是主函數,它會基于其它兩個函數的結果選擇 Primary 節點 。
下面 , 我們從 pick_primary_member 出發,看看這三個函數的具體實現邏輯 。
pick_primary_memberbool Primary_election_handler::pick_primary_member(    std::string &primary_uuid,    std::vector<Group_member_info *> *all_members_info) {  DBUG_TRACE;  bool am_i_leaving = true;#ifndef NDEBUG  int n = 0;#endif  Group_member_info *the_primary = nullptr;  std::vector<Group_member_info *>::iterator it;  std::vector<Group_member_info *>::iterator lowest_version_end;  // 基于 member_version 選擇候選節點 。  lowest_version_end =      sort_and_get_lowest_version_member_position(all_members_info);  // 基于節點權重和 server_uuid 對候選節點進行排序 。  sort_members_for_election(all_members_info, lowest_version_end);  // 遍歷所有節點 , 判斷 Primary 節點是否已定義 。  for (it = all_members_info->begin(); it != all_members_info->end(); it++) {#ifndef NDEBUG    assert(n <= 1);#endif    Group_member_info *member = *it;    // 如果當前節點是單主模式且遍歷的節點中有 Primary 節點,則將該節點賦值給 the_primary    if (local_member_info->in_primary_mode() && the_primary == nullptr &&        member->get_role() == Group_member_info::MEMBER_ROLE_PRIMARY) {      the_primary = member;#ifndef NDEBUG      n++;#endif    }    // 檢查當前節點的狀態是否為 OFFLINE 。    if (!member->get_uuid().compare(local_member_info->get_uuid())) {      am_i_leaving =          member->get_recovery_status() == Group_member_info::MEMBER_OFFLINE;    }  }  // 如果當前節點的狀態不是 OFFLINE 且 the_primary 還是為空 , 則選擇一個 Primary 節點  if (!am_i_leaving) {    if (the_primary == nullptr) {      // 因為循環的結束條件是 it != lowest_version_end 且 the_primary 為空,所以基本上會將候選節點中的第一個節點作為 Primary 節點 。      for (it = all_members_info->begin();           it != lowest_version_end && the_primary == nullptr; it++) {        Group_member_info *member_info = *it;        assert(member_info);        if (member_info && member_info->get_recovery_status() ==                               Group_member_info::MEMBER_ONLINE)          the_primary = member_info;      }    }  }  if (the_primary == nullptr) return true;  primary_uuid.assign(the_primary->get_uuid());  return false;}

推薦閱讀