概述
Gtid的核心就是给每一个事务分配一个唯一的id,根据这个ID就知道这个事务是在哪一个节点执行。鉴于它有上述的特点,可以让主从方便的做failover,集群可以通过gtid做冲突检查,因为每一个事务都有标记,dba在做集群迁移的时候也更加的方便。每执行一个事务,当前执行线程都会拿到一个唯一的(整个集群环境唯一)的事务标识符 格式:GTID=server_uuid:seq_id server_uuid实际上是一个32字节+1个字节的”/0”的字符串,在mysql启动的时候会自动去读datadir中的auto.cnf文件,如果没没有找到就自动创建一个。seq_id是给事务分配的一个序列号。 在GTID中,同一个gtid对应的事务只能被执行一次,这个和基于传统模式复制的GTID不一样,在传统模式的复制中可能会因为指定了错误position和logfile导致事务被重复执行。 Gtid是在事务commit阶段调用mysql_binlog:orderd_commit生成的。最终gtid会被写入到binlog中,注意,现在生成的是下一个事务的gtid(gtid_next)。
GTID信息的持久化
之所以需要进行GTID的持久化工作是因为gtid_executed等这些变量都是在内存中的,在数据库重启之后需要确定哪些GTID是执行过的,哪些GTID是没有执行过的。在5.7.6之前版本的数据库中,只能通过binlog来进行持久化,一个事务在写入binlog之前先重gtid_next中获取这个事务的gtid写入到binlog中,然后在进行事务的写入,在事务的commit阶段生成gtid_next的值。鉴于上诉机制,在5.7.5以下的版本中,slave端必须要开启log_slave_updates才可以。 对于5.7.6以上的版本中,引入了mysql.gtid_executed这个表来进行gtid的持久化,所有执行过的gtid都会被记录在该表中。通过gtid_executed_compression_period参数去设置执行多个个事务压缩该表一次。
相关变量
说明
gtid_owned,保存了服务器上正在实行的GTID事务列表以及拥有他们(负责执行)的server_id。主要用于多线程复制,在应用线程处理gtid事务的时候会一直拥有该事务的gtid,所以说当gtid_owned有值的时候说明当前应用线程正在应用gtid事务,在事务commit或者回滚之后就会释放。 gtid_executed,哪些gtid事务被执行了,信息是放在内存中。 gtid_purged,由于binlog被删除或者expire所导致的gtid信息丢失,对于主库来说该变量表示不再当前binlog中的gtid集合,对于备库来说该变量表示已经执行过的gtid,所以如果使用mysqldump来初始化一个开启了gtid备库的时候要特别注意在start slave之前需要先reset master然后在设置GTID_PURGED变量。
变量修改时间
关于gtid_executed修改的时间,对于主库来说,在binlog关闭的时候,gtid_executed、gtid_purged、mysql.gtid_executed都不会更新,也不能开启。在binlog开启的时候,在binlog发生rotate的时候更新gtid_executed信息,这个时候mysql.executed表不是实时更新的,但是gtid_executed变量是实时更新的。关于gtid_executed变量的更新,在ordered_commit flush阶段产生gtid,所以gtid_executed是实时更新的。关于gtid_purge变量的更新时间,在purge binlog或者超过expire时间的时候会更新gtid_purge信息。 对于从库来说,在没有开启log_slave_updates的情况下,不管有没有启用binlog结果都是gtid_executed实时更新,gtid_executed变量的值是实时更新的。关于gtid_purge变量的更新,因为没有binlog记录,所有gtid_purged的值是实时更新的,binlog开启同时log_slave_updates开启的情况下,因为有binlog event记录gtid,mysql.executed不需要实时更新,它更新的情况和主库是一样的,只是在binlog发生切换的时候或者超过expire的时候更新。 对于mysql.gtid_executed来说,在reset master的时候会清空这个表,在set global gitd_purged的时候,设置本表,同时主库发生日志轮转的时候也会修改本表。
跳过事务
1、确认事务的内容是可以跳过的 2、stop slave 3、设置gtid_next='master_uuid:xxx'; 4、注册空事务 5、设置gtid_next为automatic 6、启动slave 在gtid_executed中,如果slave自己做了写入,那么也会记录在gtid_executed中,格式为slave_uuid:xxx
##转换
传统模式修改为gtid模式
1、在所有节点上面修改enforce_gtid_consistency=warn,在设置了这个参数之后,当存在违法gtid一致性的event时候就会报错 SET @@GLOBAL.ENFORCE_GTID_CONSISTENCY = WARN; 设置完成之后运行一段时间,确认无任何报错之后可以继续进行下一步,如果有报错需要修改应用 2、在确认无告警之后 SET @@GLOBAL.ENFORCE_GTID_CONSISTENCY = ON;
3、设置gtid_mode 在哪个服务器上面先执行这个是无关紧要的 SET @@GLOBAL.GTID_MODE = OFF_PERMISSIVE; 设置了之后表示新事务是匿名的,同时运行复制时使用gtid或者匿名事务 4、设置gtid_mode=ON_PERMISSIVE; SET global gtid_mode=ON_PERMISSIVE; 先在哪个服务器上面执行没有关系,设置了之后表示新事务是gtid事务,同时允许复制时候使用gtid或者匿名事务。 5、确认匿名事务为0,所有节点 SHOW STATUS LIKE 'ONGOING_ANONYMOUS_TRANSACTION_COUNT'; 有的时候可能发现这个值为0或者不为0,但是没有关系,只要有一次为0就可以了 必须要确认所有匿名事务均已经复制完成才能继续,建议通过上述命令方式+binlog的方式,因为如果下一步执行了gtid_mode=on,那么那些未有复制的匿名事务就不能复制了。 6、开启gtid,并且写入到my.cnf文件 SET @@GLOBAL.GTID_MODE = ON;
在线将gtid模式复制修改为传统模式
1、记录复制的位置之后停止io sql线程
mh01@3307>stop slave io_thread;
查看slave状态确认,确认数据已经全部同步
Exec_Master_Log_Pos: 47477668
Read_Master_Log_Pos: 47477668
2、change master操作
mh01@3307>change master to master_auto_position=0,master_log_file='mh01-3306.000017',master_log_pos=47477668;
3、在每一台服务器上面设置gtid为on_permissive模式
set global gtid_mode=on_permissive;
4、在每一台服务器上面设置gtid为off_permissive模式
set global gtid_mode=off_permissive;
5 5、等待所有gtid_owned为空,为空表示已经没有gtid事务正在被执行。
select @@global.gtid_owned;
7、等待所有匿名事务完成 show global status like 'ONGOING_ANONYMOUS_TRANSACTION_COUNT'; 注意,有的时候会显示0或者非0,但是没有关系,在前面的步骤完成后只要有一次显示0就可以了。 8、关闭gtid set global gtid_mode=off; 重要的是要了解包含GTID事务的日志在下一步之后无法使用。 在继续之前,您必须确保拓扑中的任何位置都不存在GTID事务。 9、写入配置到my.cnf文件
##其他
simplified_binlog_gtid_recovery的问题
该参数用于定义是否支持快速gtid扫描,5.7.5之前胃false,之后为true,现在讨论什么情况下可能发生binlog全扫描的问题。 注意的问题:在gtid关闭,simplified_binlog_gtid_recovery=false的情况下,不管什么版本在重启的时候都需要进行binlog的全扫描。在binlog被删除或者被purge的情况下,5.7.6之后的数据库也需要进行binlog的全扫描,但是这个版本之前的就不需要了,因为这个版本之前的数据库不支持在线修改gtid状态。另外,如果simplified_binlog_gtid_recovery=true,那么在旧版本的数据库中可能不能获取正确的gtid值。 在5.7.6以上的数据库中,simplified_binlog_gtid_recovery=true是最好的选择。因为在获取gtid值的时候只需要扫描最新和最旧的binlog就可以了,性能很好。5.7.6以下版本的数据库中一般不会使用gtid来切换数据库。因为没有mysql.executed的支持,必须要开启log_slave_updates,会带来性能上面的影响。