数据生态:MySQL复制技术与生产实践
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

第4章 传统复制与GTID复制

MySQL 5.6之前的版本只支持传统复制,即“基于二进制日志文件(binlog file)和位置(binlog pos)的复制”。在该复制模式中,复制拓扑的初始化配置和变更、复制的高可用切换等操作都需要找到正确的二进制日志文件和位置,否则就无法正确复制。然而,寻找该位置信息的过程所涉及的操作步骤较为烦琐,于是在MySQL 5.6及其之后的版本中,出现了基于GTID的复制(为了表述上的方便,书中也简称为GTID复制)模式。它利用GTID自动定位的特性,不再需要二进制日志的位置信息,也就省去了寻找这些信息所需的烦琐步骤,极大地简化了复制拓扑的初始化配置和变更,以及复制的高可用切换等操作。本章将对这两种复制模式做基本的介绍。

4.1 传统复制

传统复制是指基于二进制日志文件和位置的复制,实际上它更像是在一些特定操作下(例如,新搭建复制拓扑、调整复制拓扑、主从切换等),告诉从库如何找到正确的复制位置的一种解决方案(GTID复制模式与此类似,但它把手动定位变更为自动定位)。每个从库使用基于二进制日志文件和位置的复制配置时,需要在使用CHANGE MASTER TO语句时,指定需要连接的主库IP地址、账号、密码、端口、二进制日志文件名和二进制日志的位置。这些信息可以保存在从库的磁盘文件中,也可以保存在从库的配置信息表中。有关细节详见第8章“从库中继日志和状态日志”。

第2章对复制的基本原理做了详细介绍,这里我们根据本章的需要,再简要阐述一遍复制的原理。

• 作为主库的MySQL实例(数据源)将数据变更作为“事件”写入二进制日志文件。二进制日志中记录的数据变更信息可能会以不同的日志记录格式存储(具体看系统变量binlog_format如何设置)。作为从库的MySQL实例读取主库的二进制日志,并在从库的本地数据库上应用主库二进制日志中的事件(日志重放)。

• 每个从库都会收到主库二进制日志的全部副本内容。每个从库自行决定如何执行其中的内容。除非另行指定一些过滤规则,否则在从库上会应用主库二进制日志中的所有事件。

• 每个从库都会记录当前对应主库的二进制日志文件位置、二进制日志文件名,以及在此文件中它已从主库读取和处理的位置(即SQL线程和I/O线程的位置)。每个从库独立地应用主库的二进制日志,相互之间不产生影响并各自记录自身应用到的位置,而且就算有从库与主库的连接发生断开或重连,也不会影响主库的操作(这里主要指主库的可用性)。

在同一个复制架构组内,所有实例的Server ID须唯一,以便唯一标识一个实例机器的二进制日志来源。这样做可以避免在较为复杂的复制架构中,重复传输与应用二进制日志(例如,环形复制和双主复制)。GTID复制模式也是如此,下文将不再赘述。

关于基于二进制日志文件和位置的复制拓扑的搭建步骤,详见第14章“搭建异步复制”。

4.2 GTID复制

GTID(Global Transaction Identifier,全局事务标识符)复制,即基于GTID实现的复制,指的是基于事务的复制。使用GTID时,在某个MySQL Server(后文为了表述上的方便,也简称为Server)上提交的事务可以被任意从库应用识别与跟踪,使用GTID复制在搭建新从库或者因故障转移到新主库时,会自动根据GTID来定位对应的二进制日志文件和位置,更准确地说,是自动寻找从库缺失的GTID SET对应的二进制日志记录,极大地降低了这些任务的复杂度。

由于GTID复制是完全基于事务的,因此也更容易确定主库和从库的数据是否一致,只要在主库上提交的所有事务在从库上也成功提交,就能保证两者之间的数据一致性。在一个持续正常运行的主从复制拓扑中,主从库之间的GTID SET一致就可以粗略地认为主从库的数据是一致的,人为的误操作除外。另外,为了获得最佳效果,建议使用基于row格式的复制。

GTID SET信息在主库与从库中都会保存。这意味着可以通过GTID SET信息来追踪二进制日志的来源。此外,一旦在给定Server中提交过某个GTID的事务,则该Server将忽略后续提交的相同GTID的事务。因此,主库上提交的事务在从库上只能应用一次,之后碰到重复的GTID时会自动跳过整个事务,这有助于保证主从库数据一致。

提示:在MySQL 5.6.x中,如果使用GTID复制,则从库必须使用系统变量log_bin来启用二进制日志记录功能,系统变量log_slave_updates将主库的二进制日志记录到从库自身的二进制日志中。因为GTID需要被持久化,MySQL 5.7.x及其之后的版本新增了一张InnoDB存储引擎的mysql.gtid_executed表来持久化GTID信息,所以可以不启用系统变量log_bin和log_slave_updates。

4.2.1 GTID的格式和存储

4.2.1.1 GTID的格式

在主从复制拓扑中,按照使用规范,主库提供读/写操作,从库提供只读操作,所以源Server通常是主库的角色。GTID是在源Server上创建的具有唯一性的标识符,并与源Server上提交的每个事务相关联。此标识符在给定的复制拓扑中的所有Server上都是唯一的。

通过GTID可以区分事务的来源(通过GTID组成中的UUID可以区分事务是由哪个Server提交的,关于GTID组成的介绍详见下文)。当主库事务被提交并将二进制日志写入二进制日志文件中时,会为其分配新的GTID,保证事务的GTID单调递增且生成的数字之间没有间隔。如果事务未写入二进制日志文件(例如,事务被过滤,或者事务是只读的),则在源Server上不会为其分配GTID。

在从库上应用主库的二进制日志时会保留主库事务的GTID,即使从库在复制事务时进行了过滤,主库事务的GTID也会在从库中持久化,即数据可能已被过滤,但是GTID仍然会被记录下来。mysql系统库下的系统表gtid_executed用于保存从库中应用的所有事务已分配的GTID。在MySQL 5.7中,当启用系统变量log_bin和log_slave_updates时,表mysql.gtid_executed中的GTID SET不包括最后一个正在使用的二进制日志文件中的GTID。

从库在应用主库的二进制日志时,碰到具有相同GTID的事务时会跳过,也就是说,主库的事务在从库上的应用不会超过1次(即不会重复执行),这可以保证主从库数据的一致性。一旦在给定Server上提交了给定GTID的事务,则该Server将不会尝试执行具有相同GTID的任何事务,也不会引发错误信息且不会执行事务中的语句。

• 如果某个给定GTID的事务正在某个Server上执行,且该事务处于活跃状态(未提交也未回滚),则在该Server上尝试执行具有相同GTID的并行事务时都将被阻塞。如果这个活跃的事务被回滚,则第一个被阻塞的并行事务会继续尝试使用该GTID来提交,在此期间,如果其他并行会话尝试使用相同的GTID来提交事务,则会被阻塞。如果给定GTID的事务被提交,则使用了相同GTID的其他并行事务的所有语句都会被跳过。

GTID由用冒号“:”分隔的UUID和TID构成,即GTID = source_id:transaction_id。

• source_id:标识事务的源Server,通常为主库的server_uuid。

• transaction_id:是由在主库上提交事务的顺序确定的序列号。例如,第一个事务的transaction_id为1(事务的GTID不能使用0作为序列号),假如在该Server上提交的事务已经到了第23个,则第23的事务的GTID为3E11FA47-71CA-11E1-9E33- C80AA9429562:23。

二进制日志中事务的GTID可以在mysqlbinlog工具的输出中查看,带GTID的复制状态可以通过PERFORMANCE_SCHEMA系统库下的复制状态表查看,例如,replication_ applier_status_by_worker表。更多信息详见第9章“通过PERFORMANCE_SCHEMA库检查复制状态”。

GTID SET指的是由一个或多个GTID列表,或一个GTID范围组成的集合。 GTID SET在MySQL Server中有几种使用方式。例如:

• 系统变量gtid_executed和gtid_purged存储的值需要使用GTID SET。

• START SLAVE子句UNTIL SQL_BEFORE_GTIDS和UNTIL SQL_AFTER_GTIDS需要使用GTID SET。其中,前者用于指定从库重放事务到GTID SET中的第一个GTID即停止,后者用于指定重放到GTID SET的最后一个GTID之后停止。

• 内置函数GTID_SUBSET()和GTID_SUBTRACT()的输入参数需要使用GTID SET。

来自同一个实例的一系列GTID可以合并成单个表达式。例如:

4.2.1.2 GTID的存储

上文提到,从MySQL 5.7开始,在mysql系统库下提供一张InnoDB引擎的gtid_executed表来持久化存储GTID。该表中的每一行数据都包含事务的始发实例UUID,以及对应的GTID SET的起始和结束事务ID;对于仅引用单个GTID的行,起始和结束事务ID相同。

安装或升级MySQL Server时,如果该表不存在,则MySQL Server会自动使用类似如下的CREATE TABLE语句创建mysql.gtid_executed表(注意,请勿自行尝试创建与修改此表,这里列出建表DDL语句仅仅是为了便于说明):

mysql.gtid_executed表仅供MySQL Server内部使用。当从库禁用二进制日志功能或关闭系统变量log_slave_updates时,每个事务的GTID都会被实时记录到该表。另外,在MySQL Server中执行FLUSH LOG语句时,二进制日志会执行切换,上一个二进制日志文件中的GTID将会被记录到该表中。当二进制日志被丢失时,该表中的GTID记录不会丢失,例如,在MySQL Server中对二进制日志执行清理时,被清理的二进制日志包含的GTID在该表中仍然会保留,但当执行RESET MASTER语句时,该表中的内容会被重置(清空)。

仅当系统变量gtid_mode被设置为ON或ON_PERMISSIVE时,GTID才存储在mysql.gtid_executed表中。是否在该表中实时存储GTID,取决于系统变量log_bin和log_slave_updates的值,详情如下:

• 如果禁用二进制日志记录(注意,禁用二进制日志需要注释掉系统变量log_bin,而不是将其设置为OFF),或者禁用log_slave_updates(设置为OFF或0),则MySQL Server在每个事务提交时将事务的GTID一并记录到该表。

• 如果启用了二进制日志记录(启用了系统变量log_bin和log_slave_updates。注意,在启动MySQL Server时,为系统变量log_bin指定了具体值,就表示启用二进制日志记录功能,而不是通过将其设置为“ON”字符串来启用二进制日志记录功能,如果这样做,最终会让所有的二进制日志文件的前缀都变成“ON”字符串),则只在切换二进制日志或关闭MySQL Server时,将先前二进制日志的所有事务的GTID写入mysql.gtid_executed表。这种情况适用于主库或启用了二进制日志记录的从库。如果MySQL Server意外停止,则当前二进制日志文件中的GTID SET不会保存在mysql.gtid_executed表中。在MySQL Server重新启动期间,将扫描二进制文件并将这些GTID记录到表中(注意,如果MySQL Server重新启动时禁用了二进制日志记录功能,则在重新启动期间不会扫描二进制日志中的GTID,也就是说二进制日志中的GTID无法被记录下来,也就无法启动复制),但不会记录所有已执行事务的GTID,最后一个二进制日志文件中的GTID不会被记录。GTID的完整记录由系统变量gtid_executed的全局值提供,始终使用全局变量@@global.gtid_executed(该变量在事务每次提交后更新)来表示MySQL Server的最新GTID状态,并且在查询该全局变量的值时不会查询mysql.gtid_executed表中的数据。

以下是一个mysql.gtid_executed表压缩示例。

通过设置系统变量gtid_executed_compression_period,可以在每次执行压缩表之前控制表中允许记录的事务数,即每隔多少个事务压缩一次,从而控制压缩率。此变量的默认值为1000,意味着在默认情况下每1000个事务之后执行表的压缩。将gtid_executed_ compression_period设置为0可以防止执行压缩,如果要关闭压缩功能,请留意磁盘空间是否充足。

启用二进制日志记录时,不会使用gtid_executed_compression_period的值,即不根据该变量定义的事务数量来执行压缩,而是在每次执行二进制日志切换时压缩mysql.gtid_ executed表。

mysql.gtid_executed表的压缩由名为thread/sql/compress_gtid_table的专用前台线程执行。此线程未在SHOW PROCESSLIST的输出中列出,但可以通过查询performance_schema. threads表来查看,类似如下内容:

4.2.2 GTID的生命周期

GTID的生命周期包括以下几个阶段:

(1)在主库上执行事务并提交。事务在提交时会被分配一个GTID,该GTID由主库的UUID和主库上尚未使用的最小非零事务序列号组成。随后,此GTID被写入主库的二进制日志(在二进制日志的GTID事件中记录,该事件排在事务的binlog event group的最前面)。如果未将事务写入二进制日志(例如,因为主库中过滤了对某个数据库操作的事务,即不写入二进制日志;或者在主库中某个事务是只读的),则不会为其分配GTID。

(2)如果为事务分配了GTID,则在提交事务时,通过二进制日志中的Gtid_log_event将GTID写入二进制日志做原子保留。如果二进制日志执行了切换或Server关闭,则会将二进制日志文件中涉及的所有事务的GTID写入mysql.gtid_executed表中保存。

(3)如果为事务分配了GTID,则在提交事务之后不久通过非原子方式将GTID添加到系统变量gtid_executed(@@global.gtid_executed)的GTID SET中,系统变量gtid_executed的GTID SET值在复制中用来标记已提交数据的状态,即表示当前数据库中的数据对应着哪些事务。当启用二进制日志记录(主库必须启用)后,系统变量gtid_executed中的GTID SET是已提交事务的完整记录,但mysql.gtid_executed表中记录的GTID SET不完整。在启用系统变量log_bin和log_slave_updates时,该表中只记录除最后一个正在使用的二进制日志之外(最新的GTID保存在当前活跃的二进制日志文件中,也即这里所说的最后一个二进制日志文件),其他所有更早的二进制日志中所涉及的GTID SET。

(4)将二进制日志数据传输到从库并存储到中继日志之后,从库读取GTID并将读取到的GTID设置为其系统变量gtid_next的值,以告诉从库必须使用此GTID记录下一个事务。注意,从库是在会话上下文中设置系统变量gtid_next的值的,而不是在全局级别设置的。

(5)从库验证在gtid_next中获得的GTID,是否已有线程获得该GTID的所有权,以便处理事务。首先,读取和检查复制事务的GTID,在处理被读取GTID的事务之前,不仅需要保证在从库上此前该GTID没有被其他事务应用过,而且还需要保证当前没有其他会话已经读取此GTID但尚未提交事务,即持有该GTID且处于活跃状态的事务。因此,如果多个客户端尝试同时应用同一事务,即拥有同一个GTID的事务,则Server只允许其中一个执行,从而解决此问题。从库的系统变量gtid_owned(@@global.gtid_owned)显示当前正在使用的每个GTID以及拥有它的线程的ID。因此,如果某个GTID已经被使用,当某个线程再次处理相同GTID的事务时不会引发错误,而是会使用自动跳过功能来忽略该事务。

(6)如果GTID尚未被使用,则从库会应用该事务。由于在重放主库二进制日志时,二进制日志的GTID EVENT中的语句“SET @@SESSION.GTID_NEXT='...';”会将从库的系统变量gtid_next设置为该语句指定的GTID,因此从库不会尝试为此事务生成新的GTID。

(7)如果在从库上启用了二进制日志记录且启用了系统变量log_slave_updates,则从库在将二进制日志回放完之后,也会将这些日志中涉及的GTID写入自身的二进制日志文件做原子保留。在二进制日志执行切换或关闭Server时,Server会将二进制日志文件中所有事务的GTID写入mysql.gtid_executed表中保存。

(8)如果在从库上禁用二进制日志记录或者禁用了系统变量log_slave_updates,则通过实时将GTID直接写入mysql.gtid_executed表来原子性地保留GTID。MySQL在每个事务中附加一条语句,将GTID插入mysql.gtid_executed表中。在这种情况下,mysql.gtid_executed表是从库上应用的事务的完整记录。请注意,在MySQL 5.7中,将GTID插入mysql.gtid_ executed表中的操作,对于DML语句是原子操作,但对于DDL语句则不是原子操作(因为DDL语句不受事务控制)。因此,如果Server在执行DDL语句时意外退出,则GTID的状态可能会变得不一致。但从MySQL 8.0开始,DDL语句和DML语句都支持原子操作。

(9)在从库上提交复制事务后不久,GTID通过非原子化的方式将其添加到从库的系统变量gtid_executed(@@global.gtid_executed)中。对于主库,此GTID SET包含所有已提交的事务集。对于从库,如果禁用二进制日志记录,就无法使用二进制日志文件来持久化GTID,但会将其实时记录到mysql.gtid_executed表中。如果启用了二进制日志记录,则mysql.gtid_executed表并不会实时记录事务的GTID,也就是说,这时该表中记录的GTID并不完整,最新的GTID不在该表中,而是在最后一个二进制日志文件中。但是要注意,系统变量gtid_executed中的GTID SET是完整的,它也是在这种情况下唯一完整记录GTID的地方。

在主库上完全过滤掉的客户端事务(例如,主库使用复制过滤参数指定过滤某个数据库)由于未分配GTID,因此不会被添加到系统变量gtid_executed中,也不会被添加到mysql.gtid_executed表中。但是,在从库上为了保证主从库数据的一致性,即使使用复制过滤参数过滤了某些事务,这些被过滤的事务对应的GTID也必须被持久化(需要确保在从库上启用了二进制日志记录且启用了系统变量log_slave_updates)。从库在将这些被过滤的事务写入自身的二进制日志时,通过Gtid_log_event事件写入仅包含BEGIN和COMMIT语句的空事务,而不包含被过滤的事务对应的数据变更日志。如果禁用二进制日志记录或禁用系统变量log_slave_updates,则被从库过滤的事务的GTID将被实时写入mysql.gtid_executed表。在从库上为被过滤的事务保留GTID,可以确保与主库拥有一致的GTID SET。另外,当从库重新连接到主库时,还可以确保不会重复拉取被过滤的事务。

在多线程复制的从库(slave_parallel_workers > 0)上,可以并行应用事务,因此复制的事务可以无序提交,除非设置slave_preserve_commit_order = 1。当发生这种情况时,系统变量gtid_executed中的GTID SET将包含多个GTID范围,它们之间存在间隙。通常,在主库或单线程复制的从库上,GTID的数值是单调递增的,数值之间没有间隙。对于多线程复制,从库上的GTID间隙仅发生在最近应用的事务中,并在复制过程中会填充。当使用STOP SLAVE语句正常停止复制线程时,会先等待应用完成正在执行的事务,然后才会真正停止复制线程,这样就能够填补GTID间隙。如果发生异常关闭,例如Server故障或使用KILL语句停止复制线程,则GTID可能会出现间隙。

客户端可以在执行事务之前将变量@@session.gtid_next设置为有效的GTID值来模拟复制事务。mysqlbinlog解析二进制日志文件时生成的GTID事件中也是使用此技术生成GTID的,这样客户端就可以重放该解析内容以保留GTID。通过客户端提交的模拟复制事务,或者将解析的二进制日志文本内容直接导入某个数据库实例,完全等同于通过复制线程提交的复制事务,并且事后也无法区分它们。

系统变量gtid_purged(@@global.gtid_purged)中的GTID SET包含已在Server上提交,但在Server上的任何二进制日志文件中都不存在的所有事务的GTID。

• 在从库上禁用二进制日志记录时,该系统变量的值表示从库上所提交的所有复制事务的GTID。

• 启用二进制日志记录时,该系统变量的值表示已写入二进制日志且已被清除的二进制日志文件中包含的事务的GTID。

• 通过语句SET @@global.gtid_purged可以为系统变量@@global.gtid_purged明确设置一个GTID SET。

Server在启动时,将对系统变量gtid_purged中的GTID SET进行初始化。每个二进制日志文件中的内容都以事件Previous_gtids_log_event开头,该事件包含先前二进制日志文件中的所有GTID SET,由前一个文件的Previous_gtids_log_event事件中的GTID与前一个文件中每个Gtid_log_event事件的GTID组成。最旧和最新的二进制日志文件的Previous_gtids_log_event事件的内容用于计算Server启动时系统变量gtid_purged对应的GTID SET。

4.2.3 GTID自动定位

GTID自动定位功能替代了在传统复制中手工指定用于确定主从库之间数据流的起始点、停止点和恢复点的文件偏移位置的工作。使用GTID时,从库需要与主库同步的所有信息(这里主要指的是主库中数据变更产生的二进制日志记录)都直接从复制数据流中自动获取。

要使用GTID复制启动从库,请不要在CHANGE MASTER TO语句中包含MASTER_LOG_FILE或MASTER_LOG_POS选项,这些选项指定二进制日志文件的名称和文件中的起始位置。对于GTID,你需要启用MASTER_AUTO_POSITION选项。有关GTID复制的配置,以及启动主库和从库的完整说明,详见第14章“搭建异步复制”。

默认情况下,MASTER_AUTO_POSITION选项被禁用。如果在从库上启用了多源复制,则需要为每个复制通道设置该选项。如果将该选项启用之后再禁用,会使从库恢复为基于文件的复制。在这种情况下,你必须指定MASTER_LOG_FILE或MASTER_LOG_POS选项中的一个或两个(从GTID复制模式切换到传统复制模式时,建议同时指定RELAY LOG相关的选项,否则还未来得及重放的中继日志会被清理,容易出现复制故障)。

当复制从库启用GTID(gtid_mode=on|on_permissive|off_permissive)并启用MASTER_ AUTO_POSITION选项时,将激活自动定位以连接主库。主库必也须设置gtid_mode = on才能使从库连接成功。在初始握手通信中,从库发送一个GTID SET,其中包含从库已经收到、已提交或两者都已完成的事务。此GTID SET等于系统变量gtid_executed(@@global. gtid_executed)中的GTID SET与接收事务时记录在performance_schema.replication_connection_ status表中的received_transaction_set字段表示的GTID的并集(received_transaction_set字段值可以使用select received_transaction_set from performance_schema.replication_connection_ status语句来查询)。

主库通过发送其二进制日志中记录的事务来响应从库,但这些事务的GTID未包含在从库发送的GTID SET中,即这些GTID未包含在从库请求连接主库时注册的GTID SET中,因为向主库注册的这些GTID代表在从库中已经被应用,或者存在于从库的中继日志中,不需要重复发送,以确保主库只发送从库中未接收或未提交的GTID对应的事务。如果从库从多个主库接收事务,则此时自动跳过事务的功能可确保事务不会被重复应用。

如果主库应发送给从库的任何事务已从主库的二进制日志中清除,则主库将错误信息ER_MASTER_HAS_PURGED_REQUIRED_GTIDS发送给从库,而且复制线程启动失败或报错中止。此时,从库无法自动从此错误中恢复,需要从另一个数据源恢复丢失的事务或者使用最新的备份(可在主库上实时备份)来恢复一个新的从库,替换旧的从库。然后,可以考虑是否修改主库上二进制日志文件的保留时长,以避免二进制日志被过早清理。

如果在事务传输期间,主库发现从库接收或提交了与主库UUID相同的事务,但主库本身没有它们的记录,则主库将错误信息ER_SLAVE_HAS_MORE_GTIDS_THAN_ MASTER发送给从库,并且复制线程启动失败或报错终止。如果主库在没有设置系统变量sync_binlog = 1的情况下碰到电源故障或操作系统崩溃,而且丢失了尚未在主库中落盘但已被从库接收的已提交事务的二进制日志时,就会发生这种情况。这可能导致主从Server具有相同GTID但是记录的数据却完全不同(主库中丢失的GTID被重新分配给客户端提交新的数据),即产生了主从库数据不一致。此时,正确的恢复方法是手动检查主库和从库的数据是否出现了不一致。如果确实出现不一致,而且主库上缺少事务,则可以将主库设置为从库,从其他拥有该缺失事务的Server中同步所缺少的事务;或者从复制拓扑中删除主库或从库,重建搭建复制拓扑。当成功恢复主库之后,根据需要选择是否将主库角色切换回去。

使用GTID复制时,关于跳过复制错误的步骤详见第24章“发生数据误操作之后的处理方案”,关于复制模式的变更步骤详见第17章“复制模式的切换”。

4.2.4 GTID复制模式的限制

由于GTID复制依赖于事务,因此在使用时不支持MySQL中的某些功能。下面是使用GTID复制时的一些限制。

1. 更新操作涉及非事务引擎

• 在同一个事务中,不能同时操作支持事务(InnoDB)和不支持事务(MyISAM)的引擎。这是由于同时操作这两类引擎时可能导致将多个GTID被分配给同一个事务。

• 主从数据库Server中相同的表使用不同的存储引擎时(其中,一个Server使用事务表,另一个Server使用非事务表),如果在非事务表上定义了触发器,可能导致事务与GTID之间的一一对应关系被破坏。

2. CREATE TABLE ... SELECT语句

使用GTID复制时,CREATE TABLE ...语句不允许同时使用SELECT语句。当binlog_format设置为statement时,CREATE TABLE ... SELECT语句是作为一个整体且只分配一个GTID的事务形式被记录在二进制日志中的,但如果使用row格式的二进制日志,则该语句将被记录为具有两个GTID的两个事务。如果主库使用statement格式的二进制日志,而从库使用row格式的二进制日志,则从库将无法正确处理事务。

3. 临时表

使用GTID时(这里指的是系统变量enforce_gtid_consistency设置为ON时),事务、存储过程、存储函数和触发器内不支持CREATE TEMPORARY TABLE和DROP TEMPORARY TABLE语句,不过可以在这些对象之外执行CREATE TEMPORARY TABLE和DROP TEMPORARY TABLE语句,但需要使用autocommit = 1自动提交。

4. 防止执行不受支持的语句

要防止执行会导致GTID复制失败的语句,则需要在启用GTID时,在整个复制拓扑的所有实例中启用系统变量enforce_gtid_consistency。当启用此系统变量之后,上述可能会导致复制出现问题的语句将直接报错,不予执行。

注意:启用系统变量enforce_gtid_consistency后,仅对需要写入二进制日志的语句强制执行GTID一致性,如果Server禁用二进制日志,或者复制过滤选项将可能导致GTID复制出问题的语句过滤掉,即最终并没有记录到二进制日志中,则不会对未记录到二进制日志中的语句强制执行GTID一致性。

5. 关于跳过事务

使用GTID时不支持使用系统变量sql_slave_skip_counter来跳过事务。如果需要跳过事务,可以用第24章“发生数据误操作之后的处理方案”中提到的注入空事务的方法来跳过事务。

6. 忽略Server实例

使用GTID时,不推荐在CHANGE MASTER TO语句中使用IGNORE_SERVER_IDS选项来忽略某个Server实例的二进制日志变更,因为在GTID复制模式下,已经应用的事务会自动被忽略。在启动GTID复制之前,请检查并清除该选项的设置,可通过SHOW SLAVE STATUS语句输出中的Replicate_Ignore_Server_Ids字段检查,如果未配置该选项,则该字段值为空。

7. GTID复制模式和mysql_upgrade

当Server启用了GTID复制模式时(gtid_mode = ON),如果需要对Server使用mysql_upgrade进行升级,则不能启用二进制日志记录(--write-binlog选项)。