从源码分析 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;}

经验总结扩展阅读