概述
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逻辑
二、步骤详解
获取集群信息
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强制切换实现逻辑所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复