此处所列代码为mysql8.0.23版本,其它版本可能略有差异。
relay_log_recovery按照官方文档描述,在server启动的时候,会创建新的relay_log,并将sql_thread指向新的relay_log坐标,并且引导io_thread将从master读取的event写入新的relay log中。从而跳过旧的relay log的apply,进而避免旧的relay log出现问题导致复制出现问题。
但是在MTS下,在出现复制gap的时候,仍然需要在旧的relay log上将gap补齐,之后,才会指向新relay log。如果当前GTID_MODE=ON && 启用了MASTER_AUTO_POSITION=1,那么不会计算gap,直接初始化并指向新的relay log。
主要的代码出现在三个函数中:
在load_mi_and_rli_from_repositories函数中,调用了
if (((thread_mask & SLAVE_SQL) != 0 || !(mi->rli->inited)) &&
mi->rli->rli_init_info(skip_received_gtid_set_recovery))
其中,在mi->rli->rli_init_info(skip_received_gtid_set_recovery)中的
if (is_relay_log_recovery && init_recovery(mi)) {
error = 1;
goto err;
}
部分,根据relay_log_recovery==True,进而调用了 init_recovery(mi)方法。在 init_recovery(mi)方法中,
根据下面的代码部分,如果当前GTID_MODE=ON && 启用了MASTER_AUTO_POSITION=1的情况下,跳过了gap的计算,直接进入到进入到初始化并指向新relay log的代码。也就是说,在这种情况下,不需要特别的补齐gap的处理,完全依赖slave启动后,通过从库与主库的gtid_executed变量差异,自动拉取缺失的gtid事务。
bool is_gtid_with_autopos_on =
(global_gtid_mode.get() == Gtid_mode::ON) && mi->is_auto_position();
。之后,进入到计算gap的过程,通过调用
error = mts_recovery_groups(rli)
计算了所有worker中是否有gap,以及需要恢复的事务量。
if (rli->mts_recovery_group_cnt) return error;
如果存在需要恢复的事务,则函数直接返回,跳过了后续的将SQL_THREAD和IO_THREAD指向新relay_log的方法调用:
if (run_relay_log_recovery) recover_relay_log(mi);
在 recover_relay_log(mi)方法中,完成将SQL_THREAD和IO_THREAD指向新relay_log。
最后,在load_mi_and_rli_from_repositories函数的末尾,调用了
if (!init_error && mi->rli->is_relay_log_recovery &&
mi->rli->mts_recovery_group_cnt)
init_error = fill_mts_gaps_and_recover(mi);
在fill_mts_gaps_and_recover(mi)中,通过
rli->until_condition = Relay_log_info::UNTIL_SQL_AFTER_MTS_GAPS;
的方式,并且通过
recovery_error = start_slave_thread(
key_thread_slave_sql, handle_slave_sql, &rli->run_lock, &rli->run_lock,
&rli->start_cond, &rli->slave_running, &rli->slave_run_id, mi);
启动SQL_THREAD线程,完成gap的恢复。之后,在fill_mts_gaps_and_recover函数接下来的部分,调用
recover_relay_log(mi);
将SQL_THREAD、IO_THREAD指向新的relay log。
到此,load_mi_and_rli_from_repositories函数就执行完了。后面的流程就是正常的启动slave线程进行复制。将IO_THREAD读取的event写入新的relay log,SQL_THREAD从新的relay log进行读取并apply。
int load_mi_and_rli_from_repositories(Master_info *mi, bool ignore_if_no_info,
int thread_mask,
bool skip_received_gtid_set_recovery) {
DBUG_TRACE;
DBUG_ASSERT(mi != nullptr && mi->rli != nullptr);
int init_error = 0;
enum_return_check check_return = ERROR_CHECKING_REPOSITORY;
THD *thd = current_thd;
/*
We need a mutex while we are changing master info parameters to
keep other threads from reading bogus info
*/
mysql_mutex_lock(&mi->data_lock);
mysql_mutex_lock(&mi->rli->data_lock);
/*
When info tables are used and autocommit= 0 we force a new
transaction start to avoid table access deadlocks when START SLAVE
is executed after RESET SLAVE.
*/
if (is_autocommit_off_and_infotables(thd)) {
if (trans_begin(thd)) {
init_error = 1;
goto end;
}
}
/*
This takes care of the startup dependency between the master_info
and relay_info. It initializes the master info if the SLAVE_IO
thread is being started and the relay log info if either the
SLAVE_SQL thread is being started or was not initialized as it is
required by the SLAVE_IO thread.
*/
check_return = mi->check_info();
if (check_return == ERROR_CHECKING_REPOSITORY) {
init_error = 1;
goto end;
}
if (!(ignore_if_no_info && check_return == REPOSITORY_DOES_NOT_EXIST)) {
if ((thread_mask & SLAVE_IO) != 0 && mi->mi_init_info()) init_error = 1;
}
check_return = mi->rli->check_info();
if (check_retur