MySQL延时复制(Delayed Replication)

延迟复制简介

即使通常MySQL复制很快,但MySQL缺省的复制存在延迟,并且用户无法缩短延迟时间。另一方面,有时却需要特意增加复制的延迟。设想这样一种场景,用户在主库上误删除了一个表,并且该操作很快被复制到从库。当用户发现这个错误时,从库早就完成了该事件重放。此时主库、从库都没有那个被误删的表了,如何恢复?如果有备份,可以幸运地从备份恢复,丢失的数据量取决于备份的新旧和从备份时间点到表被删除时间点之间该表上数据的变化量。如果没有备份呢?这种情况下,延迟复制或许可以帮上忙,作为一种恢复数据的备选方案。如果在发现问题时,从库还没有来得及重放相应的中继日志,那么就有机会在从库获得该表,继而进行恢复。这里忽略一些其它数据恢复方案,例如已经存在类似Oracle闪回技术(Flashback)在MySQL上的实现,实现方式为解析相应的二进制日志事件,生成反向的SQL语句。这些程序多为个人作品,并没有被加入MySQL发行版本中,因此在易用性、适用性、可靠性等方面还不能与原生的功能相提并论。

        MySQL支持延迟复制,以便从库故意执行比主库晚至少在指定时间间隔的事务。在MySQL 8.0中,延迟复制的方法取决于两个时间戳:immediate_commit_timestamp和original_commit_timestamp。如果复制拓扑中的所有服务器都运行MySQL 8.0.1或更高版本,则使用这些时间戳测量延迟复制。如果从库未使用这些时间戳,则执行MySQL 5.7的延迟复制。

        复制延迟默认为0秒。使用CHANGE MASTER TO MASTER_DELAY = N语句将延迟设置为N秒。从主库接收的事务比主库上的提交至少晚N秒才在从库上执行。每个事务发生延迟(不是以前MySQL版本中的事件),实际延迟仅强制在gtid_log_event或anonymous_gtid_log_event事件上。二进制日志中的每个GTID事务始终都以Gtid_log_event开头,匿名事务没有分配GTID,MySQL确保日志中的每个匿名事务都以Anonymous_gtid_log_event开头。对于事务中的其它事件,不会对它们施加任何等待时间,而是立即执行。注意,START SLAVE和STOP SLAVE立即生效并忽略任何延迟,RESET SLAVE将延迟重置为0。       

例如,下面将实验环境中一主两从半同步复制中的一个从库设置为延迟60秒复制:

mysql> change master to master_delay = 60;
ERROR 3085 (HY000): This operation cannot be performed with a running slave sql thread; run STOP SLAVE SQL_THREAD FOR CHANNEL '' first.
mysql> stop slave sql_thread;
Query OK, 0 rows affected (0.00 sec)
 
mysql> change master to master_delay = 60;
Query OK, 0 rows affected (0.01 sec)
 
mysql> start slave sql_thread;
Query OK, 0 rows affected (0.00 sec)

        联机设置延迟复制时,需要先停止sql_thread线程。现在主库执行一个事务,观察从库的变化:

-- 主
mysql> create table test.t3(a int);
Query OK, 0 rows affected (0.01 sec)
 
mysql> 
 
-- 从
mysql> desc test.t3;
ERROR 1146 (42S02): Table 'test.t3' doesn't exist
mysql> desc test.t3;
+-------+---------+------+-----+---------+-------+
| Field | Type    | Null | Key | Default | Extra |
+-------+---------+------+-----+---------+-------+
| a     | int(11) | YES  |     | NULL    |       |
+-------+---------+------+-----+---------+-------+
1 row in set (0.00 sec)

        主库上建立了一个表test.t3,DDL语句自成一个事务。60秒后,从库上才出现该表。

        从库上performance_schema模式下的replication_applier_configuration.desired_delay表列显示使用master_delay选项配置的延迟,replication_applier_status.remaining_delay表列显示剩余的延迟秒数。

-- 从
mysql> select desired_delay from performance_schema.replication_applier_configuration;
+---------------+
| desired_delay |
+---------------+
|            60 |
+---------------+
1 row in set (0.00 sec)
mysql> select remaining_delay from performance_schema.replication_applier_status;
+-----------------+
| remaining_delay |
+-----------------+
|            NULL |
+-----------------+
1 row in set (0.00 sec)
mysql>
-- 主
mysql> drop table test.t3;
Query OK, 0 rows affected (0.02 sec)
mysql> 
-- 从
mysql> select remaining_delay from performance_schema.replication_applier_status;
+-----------------+
| remaining_delay |
+-----------------+
|              54 |
+-----------------+
1 row in set (0.00 sec)
mysql> select remaining_delay from performance_schema.replication_applier_status;
+-----------------+
| remaining_delay |
+-----------------+
|              23 |
+-----------------+
1 row in set (0.00 sec)
mysql> select remaining_delay from performance_schema.replication_applier_status;
+-----------------+
| remaining_delay |
+-----------------+
|              16 |
+-----------------+
1 row in set (0.00 sec)
mysql> select remaining_delay from performance_schema.replication_applier_status;
+-----------------+
| remaining_delay |
+-----------------+
|            NULL |
+-----------------+
1 row in set (0.00 sec)
mysql>

        延迟复制可用于多种目的:

  • 防止用户在主库上出错。延迟复制时,可以将延迟的从库回滚到错误之前的时间。
  • 测试滞后时系统的行为方式。例如,在应用程序中,延迟可能是由从库设备上的重负载引起的。但是,生成此负载级别可能很困难。延迟复制可以模拟滞后而无需模拟负载。它还可用于调试与从库滞后相关的条件。
  • 检查数据库过去的快照,而不必重新加载备份。例如,通过配置延迟为一周的从库,如果需要看一下最近几天开发前的数据库样子,可以检查延迟的从库。

延迟复制的简单理解就是,客户端始终保持每条数据的同步时间不低于指定的间隔,同步是持续的。跟计划任务不同,想达到跟计划任务一样,每隔一段时间,集中执行一次是做不到的,比如一小时执行同步一次,这个是做不到的。想实现定时同步,还是写计划任务吧。

延迟复制时间戳

        MySQL 8.0提供了一种新方法,用于测量复制拓扑中的延迟,或称复制滞后。该方法取决于与写入二进制日志的每个事务(不是每个事件)的GTID相关联的以下时间戳:

  • original_commit_timestamp:将事务写入(提交)到主库二进制日志之后的自1970年1月1日00:00:00 UTC以来的微秒数。
  • immediate_commit_timestamp:将事务写入(提交)到从库的二进制日志之后的自1970年1月1日00:00:00 UTC以来的微秒数。

        mysqlbinlog的输出以两种格式显示这些时间戳,从epoch开始的微秒和TIMESTAMP格式,后者基于用户定义的时区以获得更好的可读性。例如:

#190516 15:12:18 server id 1125  end_log_pos 239 CRC32 0xc1ebcb7c       Anonymous_GTID  last_committed=0        sequence_number=1       rbr_only=no     original_committed_timestamp=1557990738835397   immediate_commit_timestamp=1557990738838735     transaction_length=192
# original_commit_timestamp=1557990738835397 (2019-05-16 15:12:18.835397 CST)
# immediate_commit_timestamp=1557990738838735 (2019-05-16 15:12:18.838735 CST)
/*!80001 SET @@session.original_commit_timestamp=1557990738835397*//*!*/;
/*!80014 SET @@session.original_server_version=80016*//*!*/;
/*!80014 SET @@session.immediate_server_version=80016*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 239

        通常,original_commit_timestamp在应用事务的所有副本上始终相同。在主从复制中,主库二进制日志中事务的original_commit_timestamp始终与其immediate_commit_timestamp相同。在从库的中继日志中,事务的original_commit_timestamp和immediate_commit_timestamp与主库的二进制日志中的相同,而在其自己的二进制日志中,事务的immediate_commit_timestamp对应于从库提交事务的时间。

        在组复制设置中,当原始主服务器是组的成员时,将在事务准备好提交时生成original_commit_timestamp。再具体说,当事务在原始主服务器上完成执行并且其写入集准备好发送给该组的所有成员以进行认证时,生成original_commit_timestamp。因此,相同的original_commit_timestamp被复制到所有服务器应用事务,并且每个服务器使用immediate_commit_timestamp在其自己的二进制日志中存储本地提交时间。

        组复制中独有的视图更改事件是一种特殊情况。包含该事件的事务由每个服务器生成,但共享相同的GTID。因此,这种事务不是先在主服务器中执行,然后复制到该组其它成员,而是该组的所有成员都执行并应用相同的事务。由于没有原始主服务器,因此这些事务的original_commit_timestamp设置为零。

监控延迟复制

        在MySQL 8之前的老版本中,监控复制的延迟(滞后)最常用的方法之一是依赖于show slave status输出中的seconds_behind_master字段。但是,当使用比传统主从复制更复杂的复制拓扑,例如组复制时,此度量标准不再适用。MySQL 8中添加的immediate_commit_timestamp和original_commit_timestamp可提供有关复制延迟的更精细的信息。监控支持这些时间戳的复制延迟的推荐方法是使用以下performance_schema模式中的表。

  • replication_connection_status:与主服务器连接的当前状态,提供有关连接线程排队到中继日志中的最后和当前事务的信息。
  • replication_applier_status_by_coordinator:协调器线程的当前状态,仅在使用多线程复制时显示该信息,提供有关协调器线程缓冲到工作队列的最后一个事务的信息,以及当前正在缓冲的事务。
  • replication_applier_status_by_worker:应用从主服务器接收事务的线程的当前状态,提供有关应用程序线程或使用多线程复制时每个工作线程应用的事务信息。

        使用这些表,可以监控相应线程处理的最后一个事务以及该线程当前正在处理的事务的信息,包括:

  • 事务的GTID。
  • 从库中继日志中检索的事务的original_commit_timestamp和immediate_commit_timestamp。
  • 线程开始处理事务的时间。
  • 对于上次处理的事务,线程完成处理它的时间。
            除Performance Schema表之外,show slave status的输出还有三个字段与延迟复制有关:
  • SQL_Delay:非负整数,表示使用CHANGE MASTER TO MASTER_DELAY = N配置的复制延迟,以秒为单位。与performance_schema.replication_applier_configuration.desired_delay值相同。
  • SQL_Remaining_Delay:当Slave_SQL_Running_State等待主执行事件后的MASTER_DELAY秒时,该字段包含一个整数,表示延迟剩余的秒数。在它他时候,此字段为NULL。与performance_schema.replication_applier_status.remaining_delay值相同。
  • Slave_SQL_Running_State:一个字符串,指示SQL线程的状态(类似于Slave_IO_State)。该值与SHOW PROCESSLIST显示的SQL线程的State值相同。
            当从库的SQL线程在执行事件之前等待延迟时,SHOW PROCESSLIST将其状态值显示为:Waiting until MASTER_DELAY seconds after master executed event。

参考链接


发布者

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注