深入理解MySQL主从原理
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

1.3.2 步骤解析

1.获取server_uuid

这一步会调用init_server_auto_options函数,用来读取auto.cnf文件,如果没有找到auto.cnf文件则会重新生成一个,生成的方法我们已经在1.1节描述过了。丢失auto.cnf文件会导致GTID发生改变,这是额外需要注意的地方。

2.读取mysql.gtid_executed表

这一步开始读取第一个 GTID 的持久化介质:mysql.gtid_executed 表,其最终调用为Gtid_table_persistor::fetch_gtids函数,原理为一行一行地读取mysql.gtid_executed表的数据并加入executed_gtids变量,但是对于主库和从库来讲,executed_gtids变量的意义不一样:

· 这个时候,主库的executed_gtids变量是不正确的,如1.2节所述,主库的mysql.gtid_executed表并不包含当前binary log的GTID,这些GTID还存在于binary log中。

· 这个时候,从库的executed_gtids变量是正确的,如1.2节所述,从库的mysql.gtid_executed表包含所有的GTID。

下面是部分代码:

3.读取binary log

这一步将会读取我们提及的第二个GTID持久化介质binary log,其读取方式为:先反向扫描,获得最后一个binary log中包含的最新GTID;然后正向扫描,获得第一个binary log中的lost GTID,在MySQL 5.7中可以理解为第一个binary log中的PREVIOUS_GTIDS_LOG_EVENT,但是会受到参数binlog_gtid_simple_recovery的影响(注意,这里笔者描述简化了,实际情况要复杂很多)。整个逻辑处于MYSQL_BIN_LOG::init_gtid_sets函数中。下面我们看一下代码,为了简捷,该代码做了大量缩减。

反向扫描:

正向扫描:

这里我们看到了参数binlog_gtid_simple_recovery是如何影响源码逻辑的(默认设置为ON)。

在MySQL 5.7中,即便在不开启GTID的情况下,PREVIOUS_GTIDS_LOG_EVENT也会存在,如果参数binlog_gtid_simple_recovery设置为ON,那么正向扫描binary log获取lost GTID的过程可以快速完成。但是如果参数binlog_gtid_simple_recovery设置为OFF,那么这个过程可能进行大量的binary log扫描,直到找到GTID_EVENT为止。

GTID模块初始化、执行purge binlog命令、超过参数expire_logs_days的大小删除binary log,这三种情况都会触发binary log的扫描行为。

MySQL 5.7中的参数binlog_gtid_simple_recovery保持默认值即可。曾经有一个案例,当每次超过参数expire_logs_days的大小而清理binary log时,系统的I/O压力都非常高,最后发现和这里参数binlog_gtid_simple_recovery=false的设置有关,在1.2节中我们已经讲述过,每次清理binary log时都会触发gtid_pured变量的设置。

4.将只在binary log的GTID加入

这一步只在主库中出现,从库中无此步骤。主要代码如下。

这一步会将那些只在binary log中存在的GITD加入mysql.gtid_executed表和gtid_executed变量。

这样,主库的mysql.gtid_executed表和gtid_executed变量也正确了。

5.初始化gtid_purged变量

初始化gtid_purged变量对于主库和从库是不同的,如下。

· 主库即上面扫描到的lost GTID,一般来讲是第一个binary log中的PREVIOUS_GTIDS_LOG_EVENT(但是会受到参数binlog_gtid_simple_recovery的影响)。

· 因为没有binary log的存在,所以从库即gtid_executed变量。

源码如下。

完成了这一步,整个初始化过程基本上就结束了,mysql.gtid_executed表、gtid_executed变量与gtid_purged变量都得到了初始化。

需要注意的是,以上步骤是笔者做了提炼和简化的,源码中要复杂很多,需要初始化的变量也多得多。