Gtid_set / Gtid_state
Gtid_set
GTID的定义由Gtid_set实现
Gtid_set = array( sidno => link_list(Interval) )
Interval = (start, end)
- Gtid_set的结构是一个以sidno为序号的数组,每个元素都指向一个Interval组成的链表,链表中的每个Interval用来存放一组GTID的区间
- Gtid_set使用int32 类型的sidno代替server_uuid作为 Interval 链表的索引
- Sid_map建立128 位的server_uuid与32位的sidno之间的映射
Gtid_state
GTID还会维护的三个变量,代码中由类Gtid_state定义
logged_gtids
【系统变量】gtid_executed
已经执行过并记录到binlog的GTID集合系统变量
lost_gtids
【系统变量】gtid_purged
从binlog删除的GTID集合。当MySQL调用 purge_logs删除binlog时,同时会更新lost_gtids的内容
owned_gtids
【系统变量】gtid_owned
正由线程执行的GTID集合
还有一个SHOW SLAVE STATUS显示的变量Retrieved_Gtid_Set:Slave收到的GTID集合
【注1】
- 不能保证最后的GTID事务是完整收到的(Retrieved_Gtid_Set的最后一个GTID),可能由于连接中断,系统崩溃等原因导致只收到“部分”事务,在MySQL 5.7.2中由修复,详见Relay log 中 GTID group 完整性检测
倒数第二个GTID事务肯定完整收到,因为如果能进行最后一个事务的传输,说明倒数第二个GTID事务传输成功
GTID的生命周期
首先,执行数据库操作时,产生一个GTID,立即记录到全局和当前线程的 gtid_owned (owned_gtids)
- 其次,提交数据库事务时,新产生的GTID 被写入 binlog,接着记录到 gtid_executed(logged_gtids),然后从全局与线程区域的 gtid_owned (owned_gtids)状态中清除
最后,如果执行了purge操作删除binlog,被删除的GTID会记录到gtid_purged(lost_gtids),这些GTID仍然包含在 gtid_executed(logged_gtids)全局状态里
Log Event
先来看一下GTID模式下的binlog event
增加了Previous_gtids_log_event,Gtid_log_event:Previous_gtids_log_event
在每个binlog的开始(Format_description_log_event之后),包含在创建该binlog之前执行过的GTID集合
Gtid_log_event
Master/Slave复制
Server/Client传输协议
传输过程为:
Client -> Server:执行命令消息(COM_BINLOG_DUMP_GTID等)
- Server -> Client:命令执行结果
这里从源码上详细的分析GTID模式下Master/Slave的复制过程,交互过程为:
— Slave:
如果设置MASTER_AUTO_POSITION方式连接Master,Slave发送的binlog_name和binlog_offset都为空,Master只使用gtids_executed定位Slave需要执行的binlog event
【调用栈】
CHANGE MASTER TO …为COM_QUERY类型
【逻辑】
- mysql_execute_command
- gtid_pre_statement_checks:GTID_STATEMENT_EXECUTE / GTID_STATEMENT_CANCEL / GTID_STATEMENT_SKIP
- 检查enforce_gtid_consistency
- 读取gtid_next
- 检查是否包含隐式提交 && gtid_next != AUTOMATIC,报错ER_CANT_DO_IMPLICIT_COMMIT_IN_TRX_WHEN_GTID_NEXT_IS_SET
- 如果gtid_next为UNDEFINED_GROUP类型,报错ER_GTID_NEXT_TYPE_UNDEFINED_GROUP
- 如果是以下类型,直接返回GTID_STATEMENT_EXECUTE
- SQLCOM_COMMIT
- SQLCOM_BEGIN
- SQLCOM_ROLLBACK
- ……
- 如果一个事务同时更新了事务表和非事务表,或者多个非事务表,必须停止事务,例如
- 检查当前事务(GTID)是否应该被跳过is_already_logged_transaction只有以下两种情况被认为应该跳过这个GTID
- gtid_next类型为GTID_GROUP & gtid_next_list为空时,owned_gtid.sidno为0(owned_gtid.sidno为0说明gtid_next被执行过,见下面【解析】)
- gtid_next类型为GTID_GROUP & gtid_next_list非空时,gtid_next_list不包含gtid_next->gtid【解析】开启GTID后
- 事务的Event Group之前的Gtid_Event会SET GTID_NEXT
- 当前THD会调用gtid_acquire_ownership_single,试图成为这个GTID的THD owner
- 如果gtid_next被执行过,gtid_state->is_logged(),break,那么owned_gtid.sidno为0
- 如果gtid_next还没有owner(owner为0),当前THD成为owner(thd->owned_gtid= gtid_next),那么owned_gtid.sidno就不为0了
- 如果gtid_next已经有owner(owner非0),等待该GTID被执行完毕(写入binlog)或者owner THD被kill掉
- SQLCOM_CHANGE_MASTER
- active_mi(Master_info,保存Master的地址、端口、用户、密码、master_uuid、master_log_name、master_log_pos、*auto_position(true/false)等信息)
- change_master
- 如果任何Slave还在运行,报错ER_SLAVE_MUST_STOP
- 当MASTER_AUTO_POSITION=1时,无法设置MASTER_LOG_FILE, MASTER_LOG_POS, RELAY_LOG_FILE以及RELAY_LOG_POS,报错ER_BAD_SLAVE_AUTO_POSITION
- 检查当MASTER_AUTO_POSITION=1时GTID是否开启,否则报错ER_AUTO_POSITION_REQUIRES_GTID_MODE_ON
- 保存当前Master的信息
- 将解析的LEX_MASTER_INFO lex_mi保存到MASTER_INFO mi
- 健壮性检查及处理
- 如果没有指定host/port/log_name/log_pos,只指定了user/password等,如何处理
- 根据need_relay_log_purge,是否purge relay log
- 执行CHANGE MASTER TO
- 将Master_info* mi刷入磁盘,如果等到执行START SLAVE才把Master_info刷盘,防止在执行START SLAVE之前如果mysqld异常关闭
- gtid_pre_statement_checks:GTID_STATEMENT_EXECUTE / GTID_STATEMENT_CANCEL / GTID_STATEMENT_SKIP
- trans_commit_stmt:提交这个事务(CHANGE MASTER TO语句)
START SLAVE
【调用栈】
【逻辑】
— Slave
start_slave:
- 会向Master发送一系列packet
- SELECTUNIX_TIMESTAMP()
- SELECT VARIABLES LIKE “SERVER_ID”
- SET @master_heartbeat_period = …
- SET @master_binlog_checksum = …
- SELECT @master_binlog_checksum
- SELECT @@GLOABLE.GTID_MODE
- ……
- COM_BINLOG_DUMP_GTID:cli_advanced_command中net_write_command发送命令
— Master
com_binlog_dump_gtid:
- slave_gtid_executed如果不是Master的logged_gtids的子集,报错ER_SLAVE_HAS_MORE_GTIDS_THAN_MASTER
- Master的lost_gitds如果不是slave_gtid_executed的子集,报错ER_MASTER_HAS_PURGED_REQUIRED_GTIDS
- 根据slave_gtid_executed,寻找第一个不在slave_gtid_executed集合中的binlog
- 获取Master binlog列表(根据master-index.log)
- 倒序遍历Master binlog列表
- 如果binlog的Previous_gtids_log_event中的GTID范围为slave_gtid_executed的子集,那么可以确定,该binlog之前的所有binlog都被Slave执行过
-
Data Replication
— Master
gtid_next类型为AUTOMATIC_GROUP
— Slave
- gtid_next类型为GTID_GROUP