![MySQL高可用实践](https://wfqqreader-1252317822.image.myqcloud.com/cover/168/36862168/b_36862168.jpg)
3.5 GTID运维
每个GTID唯一标识构成事务的一组二进制日志事件,在二进制日志中跟踪GTID事务与其事件集之间的映射。应用连接到数据库时,MySQL服务器自动跳过之前已处理的GTID事务,此行为对于自动复制定位和正确的故障转移至关重要。启用GTID也给运维带来了一些改变。
3.5.1 跳过一个事务
传统基于GTID二进制位置的复制中,从库由于某些错误导致复制中断时,一个可能的解决方案是设置sql_slave_skip_counter全局系统变量,跳过导致错误的事件,然后重启复制。但是,在启用GTID后,执行的单位由事件变为事务,因此该方法不再有效(slave_skip_errors仍然可用),并会报出以下错误:
![](https://epubservercos.yuewen.com/C96CC7/19549639908909506/epubprivate/OEBPS/Images/Figure-P90_68565.jpg?sign=1739275113-tHb4e4RshbSe0tbg11EbdjqmtkSwxs9F-0-59ee6ac1404e6c334f2ef800db1d2c56)
从错误信息可以看到,GTID跳过事务的方法是注入一个空事务,具体步骤如下:
步骤01 定位出错事务的GTID。我们需要获得从库执行的最后一个事务,方法有:show slave status \G中的Executed_Gtid_Set,show global variables like '%gtid%';中的gtid_executed,show master status;中的Executed_Gtid_Set。
步骤02 将会话级系统变量gtid_next设置为上一步的GTID,如:set gtid_next='8eed0f5b-6f9b-11e9-94a9-005056a57a4e:980058'。注意gtid_next的值只能是单个GTID。
步骤03 注入空事务:begin;commit;。
步骤04 重启复制:set gtid_next='automatic'; start slave;重启复制前需要将gtid_next设置为默认值'automatic'。下面是跳过多个事务的一个例子:
stop slave; set gtid_next='8eed0f5b-6f9b-11e9-94a9-005056a57a4e:980055'; begin;commit; set gtid_next='8eed0f5b-6f9b-11e9-94a9-005056a57a4e:980056'; begin;commit; set gtid_next='8eed0f5b-6f9b-11e9-94a9-005056a57a4e:980057'; begin;commit; set gtid_next='automatic'; start slave;
3.5.2 mysqldump导出
使用mysqldump受set-gtid-purged选项影响,set-gtid-purged选项设置为AUTO(默认值)或ON时的输出如下所示:
![](https://epubservercos.yuewen.com/C96CC7/19549639908909506/epubprivate/OEBPS/Images/Figure-P91_68568.jpg?sign=1739275113-6b7FYRRr1WpGRa0e49ykV00Q8lkENj2g-0-e770777ad403af2c7eed57e74769de07)
开始部分的SET @@SESSION.SQL_LOG_BIN=0用于防止导入数据时基于本地服务器生成新的GTID。接着GTID_PURGED被设置为备份时刻已经执行过的GTID事务,该操作将会初始化mysql.gtid_executed表、gtid_purge变量及gtid_executed变量。当mysqldump命令加入--set-gtid-purged=off选项时,则输出中不会加入SQL_LOG_BIN=0和GTID_PURGED的设置。如果要将数据导入从库进行初始化,则不能设置--set-gtid-purged=off。下面是这个选项的含义:
![](https://epubservercos.yuewen.com/C96CC7/19549639908909506/epubprivate/OEBPS/Images/Figure-P91_68569.jpg?sign=1739275113-Hl2UyVFVzDwZXWfwGKwutPM8AGEaKK1H-0-e23feb33c67dcf69319f5a2a8f299105)
细心的读者到这里可能心生疑问:为初始化从库数据,命令行使用了--all-databases选项。mysql.gtid_executed表会不会被重建,进而通过GTID_PURGED设置的mysql.gtid_executed表会重新改变,重启数据库后读取mysql.gtid_executed表可能获得错误GTID集合导致复制错误?答案也在mysqldump的输出中。
首先,如果从库实例的mysql库存在,则不会删除重建:
![](https://epubservercos.yuewen.com/C96CC7/19549639908909506/epubprivate/OEBPS/Images/Figure-P92_68571.jpg?sign=1739275113-brOyOW0Eoz1NPIPvMnaKiJqbAMRltydP-0-02d856e26fa544c682efb64db54e3c9a)
其次,如果mysql.gtid_executed表存在,则不会删除重建。最后,如果该表不存在,则创建它,但不会向其加载数据。由此得出结论,除非手工删除了mysql.gtid_executed表,否则不会因它造成复制问题,至少MySQL 8是这样。具体如下所示:
![](https://epubservercos.yuewen.com/C96CC7/19549639908909506/epubprivate/OEBPS/Images/Figure-P92_68570.jpg?sign=1739275113-2sm9f2l6Bh6sHt1XSorKwsm2X2rcyKaK-0-b4a5ad679b507da2b480a28011f3ab4e)
3.5.3 主从切换
这里分三种情况进行讨论:从库只读、从库读写并且有全部写操作的二进制日志、从库读写但写操作的二进制日志不全。
1. 从库只读
这种情况从库(新主库)没有执行过本地的事务,只需执行正常切换:
![](https://epubservercos.yuewen.com/C96CC7/19549639908909506/epubprivate/OEBPS/Images/Figure-P93_68573.jpg?sign=1739275113-i6Svp1AESjpsfPzXS6uurWy7Puclv3X2-0-21976f1c5b96e19f5bdb067cd73b8aba)
新主库会生成自己的GTID事务,此时会出现两个server_uuid对应的GTID:
![](https://epubservercos.yuewen.com/C96CC7/19549639908909506/epubprivate/OEBPS/Images/Figure-P93_68574.jpg?sign=1739275113-yg3BqCoBySPDzZMKzQXJPpt54HP5lSxk-0-113d06397acf9aab5aa7b0425eff047b)
2. 从库读写并且有全部写操作的二进制日志
![](https://epubservercos.yuewen.com/C96CC7/19549639908909506/epubprivate/OEBPS/Images/Figure-P93_68575.jpg?sign=1739275113-3Ru1oJ6m9it5P6Agh9HlJIbmWpZhWP93-0-c7386f0267318ed995884033d4c9367e)
此时从show slave status的输出中可以看到:
![](https://epubservercos.yuewen.com/C96CC7/19549639908909506/epubprivate/OEBPS/Images/Figure-P93_68576.jpg?sign=1739275113-5BLCevgpPI0LqZcVZ7Us39VlYTP753X3-0-56851385d6aea8db61e18a7f2f581140)
刚才从库执行的三个本地事务,在新从库上正常复制。因为本地事务与复制事务GTID的server_uuid部分不同,只要binlog保留完整,从库上的写操作在主从切换后可以自动复制到新的从库上,与匿名复制相比明显方便许多。
3. 从库读写但写操作的二进制日志不全
![](https://epubservercos.yuewen.com/C96CC7/19549639908909506/epubprivate/OEBPS/Images/Figure-P94_68578.jpg?sign=1739275113-2s8eEVn2fjBJoFt1OGRV7l7lSc4ABQIo-0-e6a333ab791351ca1231e2fb601a233e)
此时在show slave status的输出中报错如下:
![](https://epubservercos.yuewen.com/C96CC7/19549639908909506/epubprivate/OEBPS/Images/Figure-P94_68579.jpg?sign=1739275113-UseSfN1Iw53yeAwIDqQhaUILqsszIjvc-0-504de5dc3f372a732a113d3fd0b4878d)
真实环境中要是遇到这种情况建议还是重建从库。二进制日志文件默认的保留时间是30天(binlog_expire_logs_seconds=2592000)。一般来说从库的写操作通常是为保留一些报表结果或临时数据,这些操作的最早时间很大可能已超过三十天,在这之后进行主从切换就会出现问题。这也是建议从库readonly(只读)的原因之一。如果确实要执行比如加索引等不影响数据的操作,则可以在执行前将sql_log_bin变量设置为0,这样不会增加本地GTID:
set sql_log_bin=0; create index idx1 on test.t1(a);
但还是要强调,从库最好始终readonly。