我是靠谱客的博主 安静学姐,最近开发中收集的这篇文章主要介绍mysqlsh切换到mysql_mysqlsh:force_primary_instance强制切换实现逻辑,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

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则表示并不进行真正的切换操作,只进行相关检测

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

006ce9b5307a

force_primary_instance逻辑

二、步骤详解

获取集群信息

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); // 复制通道信息

检测是否指定了提升的主库,如果指定,检测是否与原主相同

// 获取旧主

const topology::Server *demoted = dynamic_cast(

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);

}

获取集群中实例是否可连接,分别存储在instances和invalidate_ids中,以便后续同步等待。

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

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

// 从集群中得到所有实例的元数据信息

std::vector instances_md =

get_metadata_storage()->get_all_instances(get_id());

std::list instances; // 用于保存所有存活的实现

std::list invalidate_ids; // 用于保存不可以实例id

{

std::list 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();

}

查看所有存活实例中是否有实例存在复制错误,如果存在错误将其移除到不可达实例中。

check_replication_applier_errors(srv_topology, &instances,

invalidate_error_instances, &instances_md,

&invalidate_ids);

等待所有存活的实例retrieved GTIDs都执行完成

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

wait_all_apply_retrieved_trx(&instances, timeout, invalidate_error_instances,

&instances_md, &invalidate_ids);

如果没有指定新主,则从最后保留的可用从库中选择新主

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数最多的被选为新主

如果没找到则报错

强制提升主库

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);

在新主上更新元数据信息

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

其他存活的实例,修改底层复制信息,并搭建到新主

async_change_primary(new_master.get(), instances, ar_options, nullptr,

&undo_list, dry_run);

步骤为:

确保从库只读状态;

使用change master切换到新主

清空新主的复制信息

reset_channel(new_master.get(), true, dry_run);

最后

以上就是安静学姐为你收集整理的mysqlsh切换到mysql_mysqlsh:force_primary_instance强制切换实现逻辑的全部内容,希望文章能够帮你解决mysqlsh切换到mysql_mysqlsh:force_primary_instance强制切换实现逻辑所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(58)

评论列表共有 0 条评论

立即
投稿
返回
顶部