如果复制由于复制事务中的事件问题而停止,您可以通过跳过副本上的失败事务来恢复复制。在跳过事务之前,请确保复制 I/O(接收器)线程和 SQL(应用器)线程已停止。
首先,您需要确定导致错误的复制事件。有关错误和最后成功应用的事务的详细信息记录在性能模式表 replication_applier_status_by_worker
中。您可以使用 mysqlbinlog 检索和显示在错误发生时间周围记录的事件。有关执行此操作的说明,请参见 第 9.5 节,“时间点(增量)恢复”。或者,您可以在副本上执行 SHOW RELAYLOG EVENTS
或在源上执行 SHOW BINLOG EVENTS
。
在跳过事务并重新启动副本之前,请检查以下几点
导致复制停止的事务是否来自未知或不受信任的来源?如果是,请调查原因,以防存在任何安全问题,表明不应该重新启动副本。
导致复制停止的事务是否需要应用到副本上?如果是,请对副本进行相应的更正并重新应用事务,或者手动协调副本上的数据。
导致复制停止的事务是否需要应用到源上?如果不是,请在原始发生事务的服务器上手动撤消事务。
要跳过事务,请根据需要选择以下方法之一
当使用 GTID 时(
gtid_mode
为ON
),请参见 第 19.1.7.3.1 节,“使用 GTID 跳过事务”。当不使用 GTID 或正在逐步引入 GTID 时(
gtid_mode
为OFF
、OFF_PERMISSIVE
或ON_PERMISSIVE
),请参见 第 19.1.7.3.2 节,“不使用 GTID 跳过事务”。如果您已使用
CHANGE REPLICATION SOURCE TO
语句的ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS
选项在复制通道上启用了 GTID 赋值,请参见 第 19.1.7.3.2 节,“不使用 GTID 跳过事务”。在复制通道上使用ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS
与为通道引入基于 GTID 的复制并不相同,您无法对这些通道使用基于 GTID 的复制的事务跳过方法。
要在跳过事务后重新启动复制,请执行 START REPLICA
,如果副本是多源副本,则使用 FOR CHANNEL
子句。
当使用 GTID 时(gtid_mode
为 ON
),即使事务内容被过滤掉,已提交事务的 GTID 也会保存在副本上。此功能可防止副本在使用 GTID 自动定位重新连接到源时检索到之前已过滤的事务。它还可以用于跳过副本上的事务,方法是在失败的事务位置提交一个空事务。
当您使用 ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS
选项在复制通道上启用 GTID 分配时,这种跳过事务的方法不适合。
如果失败的事务在工作线程中生成了错误,您可以直接从 Performance Schema 表 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
语句来向前移动源二进制日志位置,跳过事件或事件。
当您使用 ASSIGN_GTIDS_TO_ANONYMOUS_TRANSACTIONS
选项在复制通道上启用 GTID 分配时,这些方法也适用。
当您使用这些方法时,重要的是要了解,您并不一定会跳过一个完整的事务,正如之前描述的基于 GTID 的方法总是那样。这些基于非 GTID 的方法并不了解事务本身,而是对事件进行操作。二进制日志被组织为一系列称为事件组的组,每个事件组包含一系列事件。
对于事务表,一个事件组对应于一个事务。
对于非事务表,一个事件组对应于一个 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(接收器)和 SQL(应用器)线程正在运行,则无法发出此语句。
SET GLOBAL sql_replica_skip_counter
语句没有立即生效。当您在发出此 SET
语句后的下一次发出 START REPLICA
语句时,系统变量 sql_replica_skip_counter
的新值将被应用,并且事件将被跳过。该 START REPLICA
语句还会自动将系统变量的值重置为 0。如果副本是多源副本,则在发出该 START REPLICA
语句时,需要 FOR CHANNEL
子句。确保您指定了正确的通道名称,否则会跳过错误通道上的事件。
当您评估了失败事务的任何其他适当操作(如安全注意事项)后,请确定源二进制日志中表示适合重新启动复制的位置的坐标(文件和位置)。这可以是导致问题的事件之后的事件组的开头,或下一个事务的开头。复制 I/O(接收器)线程在下一次启动时从这些坐标开始读取源数据,跳过失败的事件。确保您已准确地识别位置,因为此语句不会考虑事件组。
按如下所示发出 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(接收器)和 SQL(应用器)线程正在运行,则无法发出此语句。如果您需要在 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;