7.5.2 使用事件位置进行时间点恢复

最后一部分第 7.5.1 节“使用二进制日志进行时间点恢复”解释了使用二进制日志执行时间点恢复的一般思路。本节通过示例详细说明操作。

例如,假设在 2020 年 5 月 27 日 13:00:00 左右执行了一条删除 table 的 SQL 语句。您可以执行时间点恢复,以将服务器恢复到 table 删除之前的状态。这些是实现该目标的一些示例步骤:

  • 恢复在感兴趣的时间点之前创建的最后一个完整备份(在我们的示例中,将其命名为tp,即 2020 年 5 月 27 日 13:00:00)。完成后,记下您将服务器还原到的二进制日志位置(以后将需要该信息)并重新启动服务器。

Note

虽然还原和服务器重新启动后,InnoDB 还会显示恢复的最后一个二进制日志位置,但这不是获取还原结束日志位置的可靠方法,因为可能会有 DDL 事件和非 InnoDB 更改发生在显示位置反映的时间之后。您的备份和还原工具应为您提供恢复的最后一个二进制日志位置:例如,如果您使用mysqlbinlog进行任务,请检查二进制日志重播的停止位置;否则,请执行以下操作:如果使用的是 MySQL Enterprise Backup,则最后一个二进制日志位置已保存在备份中。参见Point-in-Time Recovery

  • 查找与要还原数据库的时间点相对应的精确二进制日志事件位置。在我们的示例中,假设我们知道删除 table 的大致时间(tp),那么可以通过使用mysqlbinlog实用工具检查该时间前后的日志内容来找到日志位置。使用--start-datetime--stop-datetime选项在tp附近指定较短的时间,然后在输出中查找事件。例如:
shell> mysqlbinlog   --start-datetime="2020-05-27 12:59:00" --stop-datetime="2020-05-27 13:06:00" \
  --verbose /var/lib/mysql/bin.123456 | grep -C 12 "DROP TABLE"
# at 1868
#200527 13:00:30 server id 2  end_log_pos 1985 CRC32 0x8b894489 	Query	thread_id=8	exec_time=0	error_code=0
use `pets`/*!*/;
SET TIMESTAMP=1590598830/*!*/;
SET @@session.pseudo_thread_id=8/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=1436549152/*!80005 &~0x1003ff00*//*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C latin1 *//*!*/;
SET @@session.character_set_client=8,@@session.collation_connection=8,@@session.collation_server=8/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
DROP TABLE `cats` /* generated by server */
/*!*/;
# at 1985
#200527 13:05:06 server id 2  end_log_pos 2050 CRC32 0x2f8d0249 	Anonymous_GTID	last_committed=6	sequence_number=7	rbr_only=yes	original_committed_timestamp=0	immediate_commit_timestamp=0	transaction_length=0
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
# original_commit_timestamp=0 (1969-12-31 19:00:00.000000 EST)
# immediate_commit_timestamp=0 (1969-12-31 19:00:00.000000 EST)
/*!80001 SET @@session.original_commit_timestamp=0*//*!*/;
/*!80014 SET @@session.original_server_version=0*//*!*/;
/*!80014 SET @@session.immediate_server_version=0*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 2050
#200527 13:05:06 server id 2  end_log_pos 2122 CRC32 0x56280bb1 	Query	thread_id=8	exec_time=0	error_code=0

mysqlbinlog的输出中,``DROP TABLE petscats``` statement can be found in the segment of the binary log between the line#在 1868 年and#在 1985 年, which means the statement takes place *after* the log position 1868, and the log is at position 1985 after the DROP TABLE`语句中。

Note

仅使用--start-datetime--stop-datetime选项来帮助您找到感兴趣的实际事件位置。不建议使用两个选项来指定要应用的二进制日志段的范围:使用这些选项时,丢失二进制日志事件的风险更高。请改用--start-position--stop-position

  • 将二进制日志文件中的事件应用于服务器,从您在步骤 1 中找到的日志位置开始(假设它是 1006),然后在步骤 2 中您发现的时间点之前*结束。利息(即 1868 年):
shell> mysqlbinlog --start-position=1006 --stop-position=1868 /var/lib/mysql/bin.123456 \
         | mysql -u root -p

该命令从起始位置恢复到停止位置之前的所有事务。由于mysqlbinlog的输出在记录每个 SQL 语句之前都包含SET TIMESTAMP语句,因此恢复的数据和相关的 MySQL 日志将反映执行事务的原始时间。

现在,您的数据库已还原到感兴趣的时间点tp,即将删除 tablepets.cats之前。

  • 除了已完成的时间点恢复之外,如果您还希望您感兴趣的时间点之后*重新执行所有语句,请再次使用mysqlbinlogtp之后的所有事件应用于服务器。我们在第 2 步中注意到,在要跳过的语句之后,日志位于 1985 位置;我们可以将其用于--start-position选项,以便包括该位置之后的所有语句:
shell> mysqlbinlog --start-position=1985 /var/lib/mysql/bin.123456 \
         | mysql -u root -p

您的数据库已还原到二进制日志文件中记录的最新语句,但是跳过了所选事件。