如果复制由于某个事务中的事件问题而停止,可以通过在副本上跳过失败的事务来恢复复制。在跳过事务之前,确保复制I/O(receiver)线程和SQL(applier)线程都已停止。
首先,您需要确定引发错误的复制事件。错误详细信息和最后一个成功应用的事务记录在性能模式表replication_applier_status_by_worker
中。您可以使用mysqlbinlog检索和显示错误发生时的事件。有关如何执行此操作的说明,请参见第9.5节,“点时间(增量)恢复”。或者,您可以在副本上发出SHOW RELAYLOG EVENTS
,或在源上发出SHOW BINLOG EVENTS
。
在跳过事务并重新启动副本之前,请检查以下几点:
-
停止复制的事务来自未知或不可信的来源?如果是,那么调查原因,以免存在任何安全考虑因素,表明副本不应该重新启动。
-
停止复制的事务需要在副本上应用?如果是,那么要么纠正错误并重新应用事务,要么手动在副本上调和数据。
-
停止复制的事务需要在源上应用?如果不是,那么手动撤销事务在原始服务器上。
要跳过事务,可以选择以下方法之一:
-
当使用GTIDs时(
gtid_mode
为ON
),请参见第19.1.7.3.1节,“使用GTIDs跳过事务”。 -
当不使用GTIDs或正在逐步引入GTIDs时(
gtid_mode
为OFF
、OFF_PERMISSIVE
或ON_PERMISSIVE
),请参见第19.1.7.3.2节,“不使用GTIDs跳过事务”。 -
如果您在复制通道上启用了GTID分配使用
CHANGE REPLICATION SOURCE TO
语句的ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS
选项,请参见第19.1.7.3.2节,“不使用GTIDs跳过事务”。在复制通道上使用ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS
与引入GTID-based复制不同,并且您不能使用GTID-based复制的跳过事务方法。
要重新启动复制,请发出START REPLICA
,如果副本是多源副本,请添加FOR CHANNEL
子句。
当使用GTIDs时(gtid_mode
为ON
),副本上提交的事务的GTID将被持久化,即使事务的内容被过滤掉。这项功能可以防止副本在使用GTID自动定位时检索到之前过滤的事务。它也可以用来跳过副本上的事务,方法是提交一个空事务来代替失败的事务。
这种跳过事务的方法不适用于在复制通道上启用GTID分配时,使用ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS
选项的CHANGE REPLICATION SOURCE TO
语句。
如果失败的事务在工作线程中生成了错误,可以从性能模式表replication_applier_status_by_worker
的APPLYING_TRANSACTION
字段中直接获取其GTID。要查看事务的内容,请在副本上发出SHOW RELAYLOG EVENTS
语句或在源上发出SHOW BINLOG EVENTS
语句,并在输出中搜索该GTID前的交易。
当您评估了失败的事务以进行适当的操作(例如安全考虑)后,要跳过它,请在副本上提交一个空事务,该事务具有与失败事务相同的GTID。例如:
SET GTID_NEXT='aaa-bbb-ccc-ddd:N';
BEGIN;
COMMIT;
SET GTID_NEXT='AUTOMATIC';
副本上的空事务的存在意味着,当您发出START REPLICA
语句以重新启动复制时,副本将使用自动跳过函数忽略失败的事务,因为它看到一个具有该GTID的事务已经被应用。如果副本是一个多源副本,您不需要在提交空事务时指定通道名称,但需要在发出START REPLICA
语句时指定通道名称。
注意,如果在这个副本上启用了二进制日志记录,那么空事务将进入复制流,如果副本在未来成为源或主服务器。如果您需要避免这种可能性,请考虑刷新和清除副本的二进制日志,如下所示:
FLUSH LOGS;
PURGE BINARY LOGS TO 'binlog.000146';
空事务的GTID将被持久化,但事务本身将被清除二进制日志文件。
要跳过失败的事务,当GTID不在使用或正在被引入时(gtid_mode
是OFF
、OFF_PERMISSIVE
或ON_PERMISSIVE
),您可以通过发出SET GLOBAL sql_replica_skip_counter
语句来跳过指定数量的事件。或者,您可以通过发出CHANGE REPLICATION SOURCE TO
语句来移动源二进制日志位置。
这些方法也适用于在复制通道上启用GTID分配时,使用CHANGE REPLICATION SOURCE TO
语句的ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS
选项。
当您使用这些方法时,需要了解您不是跳过完整的事务,而是跳过事件。这些非GTID-based方法不知道事务,而是操作事件。二进制日志被组织成事件组的序列,每个事件组由事件序列组成。
-
对于事务表,每个事件组对应一个事务。
-
对于非事务表,每个事件组对应一个SQL语句。
单个事务可以包含事务表和非事务表的更改。
当您使用 SET GLOBAL sql_replica_skip_counter
语句跳过事件并且结果位置在事件组的中间时,副本将继续跳过事件直到达到事件组的结尾。然后,执行将从下一个事件组开始。CHANGE REPLICATION SOURCE TO
语句不具有这种功能,因此您必须小心地确定正确的位置来重新启动复制在事件组的开始处。然而,使用 CHANGE REPLICATION SOURCE TO
语句意味着您不需要计算需要跳过的事件数,如同使用 SET GLOBAL sql_replica_skip_counter
一样,而是可以指定重新启动的位置。
当您已经评估了失败的事务以进行其他适当的操作(例如安全考虑),计算需要跳过的事件数。一个事件通常对应于二进制日志中的一个 SQL 语句,但请注意,使用 AUTO_INCREMENT
或 LAST_INSERT_ID()
的语句在二进制日志中计为两个事件。当二进制日志事务压缩在使用时,压缩的事务有效负载(Transaction_payload_event
)被计为一个计数器值,因此所有内部事件都被跳过作为一个单元。
如果您想跳过整个事务,可以计算到事务结束的事件数,或者可以跳过相关的事件组。请记住,使用 SET GLOBAL sql_replica_skip_counter
,副本将继续跳过到事件组的结尾。确保您不跳过太远,进入下一个事件组或事务,以免也被跳过。
发出以下 SET
语句,其中 N
是要跳过的事件数来自源:
SET GLOBAL sql_replica_skip_counter = N
如果 gtid_mode=ON
被设置,或者复制 I/O(receiver)和 SQL(applier)线程正在运行时,无法发出该语句。
该 SET GLOBAL sql_replica_skip_counter
语句没有立即效果。当您发出下一个 START REPLICA
语句时,该系统变量的新值将被应用,事件将被跳过。该 START REPLICA
语句也将自动将系统变量的值设置回 0。如果副本是一个多源副本,当您发出该 START REPLICA
语句时,需要使用 FOR CHANNEL
子句指定正确的通道,否则事件将在错误的通道上被跳过。
当您已经评估了失败的事务以进行其他适当的操作(例如安全考虑),确定源的二进制日志中的坐标(文件和位置),以便重新启动复制。这可以是导致问题的事件后的事件组的开始,或者下一个事务的开始。复制 I/O(receiver)线程将从这些坐标开始读取源的二进制日志,跳过失败的事件。确保您已经准确地确定了位置,因为该语句不考虑事件组。
发出以下 CHANGE REPLICATION SOURCE TO
语句,其中 source_log_name
是包含重新启动位置的二进制日志文件,source_log_pos
是二进制日志文件中重新启动位置的数字表示:
CHANGE REPLICATION SOURCE TO SOURCE_LOG_FILE='source_log_name', SOURCE_LOG_POS=source_log_pos;
如果副本是一个多源副本,您必须使用 FOR CHANNEL
子句来指定适当的通道在 CHANGE REPLICATION SOURCE TO
语句中。
如果 SOURCE_AUTO_POSITION
是 1
,或者复制 I/O(receiver)和 SQL(applier)线程正在运行,则不能发出该语句。如果您需要在 SOURCE_AUTO_POSITION=1
时使用跳过事务的方法,可以在发出语句时将设置更改为 SOURCE_AUTO_POSITION=0
,然后再将其更改回来。例如:
CHANGE REPLICATION SOURCE TO SOURCE_AUTO_POSITION=0, SOURCE_LOG_FILE='binlog.000145', SOURCE_LOG_POS=235;
CHANGE REPLICATION SOURCE TO SOURCE_AUTO_POSITION=1;