MySQL relay_log_recovery源码分析

本文分析了MySQL 8.0.23版本中relay_log_recovery的源码实现,讲解了在MTS模式下如何处理复制gap,以及在GTID_MODE=ON和MASTER_AUTO_POSITION=1时如何跳过gap计算,直接指向新relay log。
摘要由CSDN通过智能技术生成

此处所列代码为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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值