美文网首页
mysqlsh:force_primary_instance强制

mysqlsh:force_primary_instance强制

作者: 真之棒2016 | 来源:发表于2020-07-23 14:26 被阅读0次

author:sufei

版本:mysql shell 8.0.19


本文是接续上一篇《set_primary_instance安全切换实现逻辑》。

主要讲解mysqlsh实现安全切换逻辑,即force_primary_instance的内部实现逻辑。

一、force_primary_instance处理逻辑

​ 执行整个强制切换的入口函数为Replica_set_impl::force_primary_instance,

void Replica_set_impl::force_primary_instance(const std::string &instance_def,
                                              uint32_t timeout,
                                              bool invalidate_error_instances,
                                              bool dry_run)

其中参数:

instance_def 表示切换到的新主;

timeout 表示gtid同步超时时间

invalidate_error_instances 表示是否忽略存在错误的从库

dry_run 如果为true则表示并不进行真正的切换操作,只进行相关检测

下面是其主要处理逻辑框图

force_primary_instance逻辑

二、步骤详解

  1. 获取集群信息
topology::Server_global_topology *srv_topology = nullptr;
auto topology = setup_topology_manager(&srv_topology);

在这其中会获取每个实例的复制信息,具体调用栈如下:

setup_topology_manager
|--->scan_global_topology
|------>load_cluster     //获取集群所有实例元数据信息
|------>check_servers
|--------->check_server
|------------>load_instance_state  // 获取实例状态信息具体内容如下:

instance->server_id = *conn->get_sysvar_int("server_id");        // serverid获取
instance->offline_mode = conn->get_sysvar_bool("offline_mode");  // offline_mode变量
instance->read_only = conn->get_sysvar_bool("read_only");        // 只读信息
instance->super_read_only = conn->get_sysvar_bool("super_read_only");
instance->executed_gtid_set = mysqlshdk::mysql::get_executed_gtid_set(*conn);  // gtid
load_instance_channels(instance, conn, m_repl_channel_name);   // 复制通道信息
  1. 检测是否指定了提升的主库,如果指定,检测是否与原主相同
// 获取旧主
const topology::Server *demoted = dynamic_cast<const topology::Server *>(
      srv_topology->get_primary_master_node());

const topology::Server *promoted = nullptr;

  // Check if the specified target instance is available.
if (!instance_def.empty()) {
    // 如果指定了新主,则从集群中得到指定新主信息
    promoted = check_target_member(srv_topology, instance_def);
    // 如果新旧主一致,则直接返回
    if (promoted == demoted)
      throw shcore::Exception(promoted->label + " is already the PRIMARY",
                              SHERR_DBA_BAD_ASYNC_PRIMARY_CANDIDATE);
}
  1. 获取集群中实例是否可连接,分别存储在instances和invalidate_ids中,以便后续同步等待。

当然这里invalidate_error_instances需要设置为true,也就是在执行force_primary_instance时,设置了invalidateErrorInstances选项为true,忽略错误实例

<font color=red>如果没有设置,只要检测到任何一个从库不可达或者出错都将抛出异常,切换失败</font>。下面逻辑也是相同

// 从集群中得到所有实例的元数据信息
std::vector<Instance_metadata> instances_md =
      get_metadata_storage()->get_all_instances(get_id());
std::list<Scoped_instance> instances;    // 用于保存所有存活的实现
std::list<Instance_id> invalidate_ids;   // 用于保存不可以实例id

{
    std::list<Instance_metadata> unreachable;

    console->print_info("* Connecting to replicaset instances");
    // 收集实例信息,unreachable保存不可达的实例
    instances = connect_all_members(timeout + 5, true, &unreachable);

    if (!unreachable.empty()) {
      if (!invalidate_error_instances) {
        console->print_error(
            "Could not connect to one or more SECONDARY instances. Use the "
            "'invalidateErrorInstances' option to perform the failover anyway "
            "by skipping and invalidating unreachable instances.");
        throw shcore::Exception("One or more instances are unreachable",
                                SHERR_DBA_UNREACHABLE_INSTANCES);
      }
    }

    for (const auto &i : unreachable) {
      console->print_note(
          i.label +
          " will be invalidated and must be removed from the replicaset.");

      invalidate_ids.push_back(i.id); // 不可达实例添加到invalidate_ids中

      // Remove invalidated instance from the instance metadata list.
      instances_md.erase(
          std::remove_if(instances_md.begin(), instances_md.end(),
                         [&i](const Instance_metadata &i_md) {
                           return i_md.uuid == i.uuid;
                         }),
          instances_md.end());
    }
    console->print_info();
  }
  1. 查看所有存活实例中是否有实例存在复制错误,如果存在错误将其移除到不可达实例中。
check_replication_applier_errors(srv_topology, &instances,
                                   invalidate_error_instances, &instances_md,
                                   &invalidate_ids);
  1. 等待所有存活的实例retrieved GTIDs都执行完成

如果设置了invalidateErrorInstances,则当存活实例中未同步完成的也就移除到invalidate_ids中,不在保留在可用从库instances中

wait_all_apply_retrieved_trx(&instances, timeout, invalidate_error_instances,
                               &instances_md, &invalidate_ids);
  1. 如果没有指定新主,则从最后保留的可用从库中选择新主
if (instance_def.empty()) {
    console->print_info(
        "* Searching instance with the most up-to-date transaction set");
    promoted = srv_topology->find_failover_candidate(instances);
}
// 如果选中提升的从库不可用则报错
if (promoted->invalidated)
    throw shcore::Exception(promoted->label + " was invalidated by a failover",
                            SHERR_DBA_ASYNC_MEMBER_INVALIDATED);

​ 此时挑选的逻辑为:

  • 候选实例执行SELECT @@GLOBAL.GTID_EXECUTED,得到最新实例gtid执行情况
  • 通过estimate_gtid_set_size函数给gtidset评估一个大小,这里的逻辑就是评估gtid数量(可能不够严谨)
  • gtid数最多的被选为新主
  • 如果没找到则报错
  1. 强制提升主库
async_force_primary(new_master.get(), ar_options, dry_run);

主要逻辑:

  • 关闭只读
instance->set_sysvar("SUPER_READ_ONLY", false,
                         mysqlshdk::mysql::Var_qualifier::PERSIST);
// Set SUPER_READ_ONLY=1 will also set READ_ONLY
instance->set_sysvar("READ_ONLY", false,
                         mysqlshdk::mysql::Var_qualifier::PERSIST);
  • 停止复制,注意这里并没有清空复制配置信息,便于操作回滚
stop_channel(promoted, k_channel_name, dry_run);
  1. 在新主上更新元数据信息
m_metadata_storage->record_async_primary_forced_switch(
            promoted->instance_id, invalidate_ids);

这里与set_primary_instance类似,只是view_id的编号变化方式不同

在set_primary_instance中view_id = last_aclvid + 1;

在强制切换中新的view_id为(trunc(view_id / (MAX_UINT16 + 1), 0) + 1) * (MAX_UINT16 + 1),也就是低16为清空,高16为加1

  1. 其他存活的实例,修改底层复制信息,并搭建到新主
async_change_primary(new_master.get(), instances, ar_options, nullptr,
                         &undo_list, dry_run);

步骤为:

  • 确保从库只读状态;
  • 使用change master切换到新主
  1. 清空新主的复制信息
reset_channel(new_master.get(), true, dry_run);

相关文章

  • mysqlsh:force_primary_instance强制

    author:sufei 版本:mysql shell 8.0.19 本文是接续上一篇《set_primary_i...

  • mysqlsh(mysql shell)学习

    简介 mysqlsh是个msyql的命令行工具,好像很智能,官方推荐在5.7和8版本中使用.我对这个有些不了解,以...

  • mysqlsh:set_primary_instance安全切换

    author:sufei 版本:mysql shell 8.0.19 本文与接下来的《force_primary_...

  • js 实用小套路,你需要的开发小技巧

    强制类型转换 强制转换为Boolean 用 !! 强制转换为Number 用 + 强制转换为String 用 ""...

  • app升级测试点

    作者:Gakki app升级测试点 主要分为强制强制更新与非强制更新 强制更新测试点:强制升级是否可以升级成功:从...

  • 强制

    什么是强制学习,什么是非强制学习? 非强制学习在某种意义上就是主动学习,强制学习也就是被动学习,那么他们之间...

  • 强制

    强制别人行为将是一件坏事情,即使在公理看起来是对的,因为人都有暂时凝固的价值观在指挥着他的直觉与行动!违背直觉一定...

  • 强制

    总是有办法强制性融合,但是自圆其说就有些难了。 明日需要将所有的事件都看一遍才好。 这就是强制事件。 无法简约和跳...

  • 强制

    版本【2.0】版本 公告【软件已更新】公告

  • 强制

    强制,茫然,劝阻,焦黄,着迷,耐心,修理,如愿。 1,强制。 作文也可以是强制的,如,高考,不管你这些考生有没有准...

网友评论

      本文标题:mysqlsh:force_primary_instance强制

      本文链接:https://www.haomeiwen.com/subject/ktchlktx.html