MySQL 5.7.5 : GTID_EXECUTED系统表

Gtid是从5.6开始推出的杀手级特性,通过GTID特性,极大的提升了主备切换的效率和一致性,想了解GTID内部实现的朋友可以参阅我之前写的这篇博客:http://mysqllover.com/?p=594

在MySQL5.7.5里引入了一个新的系统表GTID_EXECUTED:

root@mysql 11:29:40>SHOW CREATE TABLE mysql.gtid_executed\G

*************************** 1. row ***************************

       Table: gtid_executed

Create Table: CREATE TABLE `gtid_executed` (

  `source_uuid` char(36) NOT NULL COMMENT ‘uuid of the source where the transaction was originally executed.’,

  `interval_start` bigint(20) NOT NULL COMMENT ‘First number of interval.’,

  `interval_end` bigint(20) NOT NULL COMMENT ‘Last number of interval.’,

  PRIMARY KEY (`source_uuid`,`interval_start`)

) ENGINE=InnoDB DEFAULT CHARSET=utf8

1 row in set (0.00 sec)

表的描述参考官方文档:

http://dev.mysql.com/doc/refman/5.7/en/replication-gtids-concepts.html#replication-gtids-gtid-executed-table

对应worklog: http://dev.mysql.com/worklog/task/?id=6559

该特性最大的用处就是,如果我们的备库只是作为只读节点,那么就可以关闭复制线程的binlog来降低复制的开销(关闭log_slave_updates),同时还会维持SQL线程拥有的事务GTID到系统表中。这样即使主备切换,我们也不会丢失GTID。而在之前的版本中,是不允许在打开GTID时关闭log_slave_updates的。

当然了,如果你的备库是级联的一部分,也就是说,是另外一台备库的主库,这个特性就发挥不了作用啦。

下面简单过一下相关的代码实现逻辑

0.background

新增源代码文件sql/rpl_gtid_persist.cc 及 sql/rpl_gtid_persist.h, 用于处理新增的GTID持久化逻辑。

新增类Gtid_table_persistor 及类对象gtid_table_persistor, 用于操作新增的系统表

gtid_table_persistor

Gtid_state增加新成员:

executed_gtids 所有已经执行和存储到gtid_executed表中的GTID集合
gtids_only_in_table 只存在于gtid_executed系统表,不存在于binlog文件的GTID集合
previous_gtids_logged 之前一个Binlog包含的gtid集合,不包含gtids_only_in_table ref :MYSQL_BIN_LOG::open_binlog

1.记录GTID_EXECUTED表

根据文档描述,分三种情况使用该表:

一种是禁止binlog时(不是简单的设置sql_log_bin),该表记录每个事务拥有的GTID,并定期进行表压缩。

第二种是打开binlog,并且log_slave_updates打开,在binlog文件rotate或者shutdown实例时,记录GTID集合

第三种是打开Binlog,但log_slave_updates关闭,除了在rotate或shutdown时记录之前的GTID SET 还会记录SQL线程执行的事务的GTID SET.

在事务提交,进入函数ha_commit_trans时:

满足如下条件,事务拥有的gtid被记录入gtid_executed表中

1547   /*

1548     Save transaction owned gtid into table before transaction prepare

1549     if binlog is disabled, or binlog is enabled and log_slave_updates

1550     is disabled with slave SQL thread or slave worker thread.

1551   */

1552   if ((!opt_bin_log || (thd->slave_thread && !opt_log_slave_updates)) &&

1553       (all || !thd->in_multi_stmt_transaction_mode()) &&

1554       !thd->owned_gtid.is_null() && !thd->is_operating_gtid_table)

1555   {

1556     error= gtid_state->save(thd);

1557     need_clear_owned_gtid= true;

1558   }

在完成innodb commit后,调用Gtid_state::update_on_commit,从函数逻辑可以看到:

#如果binlog关闭,所有拥有GTID的事务都会加到executed_gtids集合中;

#如果binlog打开,并且log_slave_updates是关闭时,复制sql线程会将其拥有的GTID加到executed_gtids集合和gtids_only_in_table中。

当我们手动执行FLUSH LOGS,触发一次binlog rotate,也会写入到gtid_executed表中,堆栈如下:

MYSQL_BIN_LOG::new_file_impl

Gtid_state::save_gtids_of_last_binlog_into_table

Gtid_table_persistor::save

2.压缩GTID表

如果备库复制线程的log_slave_updates关闭,那么会大量插入GTID到系统表中(为什么用插入,而不用update呢 ? 原因是为了避免多线程复制时更新表热点记录)。因此需要定期将插入其中的GTID合并成一个集合。这是由一个独立线程来完成的。

新引入参数executed_gtids_compression_period,  用于控制线程多久被唤醒来压缩表gtid_executed,默认值为1000,表示每执行1000个事务后进行一次压缩。

在函数Gtid_table_persistor::save(THD *thd, Gtid *gtid)中进行判断,是否需要唤醒压缩线程

创建线程函数create_compress_gtid_table_thread

中断线程函数terminate_compress_gtid_table_thread

线程入口函数:compress_gtid_table

线程唤醒后,会调用Gtid_table_persistor::compress —>compress_in_single_transaction进行GTID合并。

3.设置GTID_PURGED

mysql实例启动时初始化,会同时读取binlog文件和gtid_executed表来初始化集合

quoted code  (mysqld.cc)

第一种情况:binlog打开时

4484       if (mysql_bin_log.init_gtid_sets(&logged_gtids_binlog,

4485                                        &purged_gtids_binlog,

4486                                        NULL,

4487                                        opt_master_verify_checksum,

4488                                        true/*true=need lock*/,

4489                                        true) ||

4490           gtid_state->fetch_gtids(executed_gtids) == -1)

4491         unireg_abort(1);

完成初始化logged_gtids_binlog, purged_gtids_binlog 以及从gtid_executed表中读取GTID到executed_gtids后。

logged_gtids_binlog收集自binlog,表示当前记录在binlog中的gtid集合。如果logged_gtids_binlog不为空,且不是executed_gtids的子集,那么未保存到表中的gtid集合为:

unsaved_gtids_in_table.add_gtid_set(&logged_gtids_binlog);

unsaved_gtids_in_table.remove_gtid_set(executed_gtids);

然后将这一部分保存到gtid_executed表中。

第二种情况:binlog关闭

4585     else if (gtid_mode > GTID_MODE_OFF)

4586     {

4587       /*

4588         If gtid_mode is enabled and binlog is disabled, initialize

4589         executed_gtids from gtid_executed table.

4590       */

4591       if (gtid_state->fetch_gtids(executed_gtids) == -1)

4592         unireg_abort(1);

4593     }

4594   }

在初始化GTID_PURGED时,当binglog打开,从lost_gtids获取,否则从executed_gtids获取

参考:Sys_var_gtid_purged::global_value_ptr

4. 重置

RESET MASTER会重置该表

backtrace :

reset_master

MYSQL_BIN_LOG::reset_logs

Gtid_state::clear

Gtid_table_persistor::reset

5. 相关代码:

http://bazaar.launchpad.net/~mysql/mysql-server/5.7/revision/8142

原创文章,转载请注明: 转载自Simple Life

本文链接地址: MySQL 5.7.5 : GTID_EXECUTED系统表

Post Footer automatically generated by wp-posturl plugin for wordpress.


Comments

Leave a Reply

Your email address will not be published. Name and email are required


Current month ye@r day *