# 7.5.2 使用事件位置的时间点恢复

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

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

  1. 恢复在感兴趣的时间点之前创建的最后一个完整备份(称之为t<sub>p</sub>,在我们的示例中为 2020 年 3 月 11 日 20:06:00)。完成后,记下您已将服务器恢复到的二进制日志位置以供以后使用,然后重新启动服务器。

    笔记

    在恢复和服务器重启后,InnoDB 也会显示最后恢复的二进制日志位置,即不是获取还原的结束日志位置的可靠方法,因为在显示位置反映的时间之后可能发生了 DDL 事件和非 InnoDB 更改。您的备份和恢复工具应该为您提供恢复的最后一个二进制日志位置:例如,如果您正在使用mysqlbinlog对于任务,检查二进制日志重放的停止位置;如果您使用的是 MySQL Enterprise Backup,则最后一个二进制日志位置已保存在您的备份中。看时间点恢复 (opens new window).

  2. 找到与您要将数据库还原到的时间点相对应的精确二进制日志事件位置。在我们的示例中,假设我们知道删除表的大致时间(t<sub>p</sub>),我们可以通过检查当时的日志内容来找到日志位置mysqlbinlog效用。使用--开始日期时间--停止日期时间指定一个短时间段的选项t<sub>p</sub>,然后在输出中查找事件。例如:

    $> mysqlbinlog --start-datetime="2020-03-11 20:05:00" \
                       --stop-datetime="2020-03-11 20:08:00" --verbose \
             /var/lib/mysql/bin.123456 | grep -C 15 "DROP TABLE"
    
    /*!80014 SET @@session.original_server_version=80019*//*!*/;
    /*!80014 SET @@session.immediate_server_version=80019*//*!*/;
    SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
    # at 232
    #200311 20:06:20 server id 1  end_log_pos 355 CRC32 0x2fc1e5ea 	Query	thread_id=16	exec_time=0	error_code=0
    SET TIMESTAMP=1583971580/*!*/;
    SET @@session.pseudo_thread_id=16/*!*/;
    SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
    SET @@session.sql_mode=1168113696/*!*/;
    SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
    /*!\C utf8mb4 *//*!*/;
    SET @@session.character_set_client=255,@@session.collation_connection=255,@@session.collation_server=255/*!*/;
    SET @@session.lc_time_names=0/*!*/;
    SET @@session.collation_database=DEFAULT/*!*/;
    /*!80011 SET @@session.default_collation_for_utf8mb4=255*//*!*/;
    DROP TABLE `pets`.`cats` /* generated by server */
    /*!*/;
    # at 355
    #200311 20:07:48 server id 1  end_log_pos 434 CRC32 0x123d65df 	Anonymous_GTID	last_committed=1	sequence_number=2	rbr_only=no	original_committed_timestamp=1583971668462467	immediate_commit_timestamp=1583971668462467	transaction_length=473
    # original_commit_timestamp=1583971668462467 (2020-03-11 20:07:48.462467 EDT)
    # immediate_commit_timestamp=1583971668462467 (2020-03-11 20:07:48.462467 EDT)
    /*!80001 SET @@session.original_commit_timestamp=1583971668462467*//*!*/;
    /*!80014 SET @@session.original_server_version=80019*//*!*/;
    /*!80014 SET @@session.immediate_server_version=80019*//*!*/;
    SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
    # at 434
    #200311 20:07:48 server id 1  end_log_pos 828 CRC32 0x57fac9ac 	Query	thread_id=16	exec_time=0	error_code=0	Xid = 217
    use `pets`/*!*/;
    SET TIMESTAMP=1583971668/*!*/;
    /*!80013 SET @@session.sql_require_primary_key=0*//*!*/;
    CREATE TABLE dogs
    

    从输出mysqlbinlog, 这删除表宠物.猫``语句可以在二进制日志的行之间的段中找到# 在 232# 在 355,这意味着语句发生日志位置 232,日志在位置 355 之后删除表陈述。

    笔记

    仅使用--开始日期时间--停止日期时间帮助您找到感兴趣的实际事件位置的选项。不建议使用这两个选项来指定要应用的二进制日志段的范围:使用这些选项时丢失二进制日志事件的风险更高。采用--开始位置--停止位置反而。

  3. 将二进制日志文件中的事件应用到服务器,从您在步骤 1 中找到的日志位置开始(假设它是 155),并在您在步骤 2 中找到的位置结束,即您感兴趣的时间点(即 232):

    $> mysqlbinlog --start-position=155 --stop-position=232 /var/lib/mysql/bin.123456 \
             | mysql -u root -p
    

    该命令恢复从起始位置直到停止位置之前的所有事务。因为输出mysqlbinlog包括设置时间戳记录每个 SQL 语句之前的语句,恢复的数据和相关的 MySQL 日志反映了事务执行的原始时间。

    您的数据库现在已恢复到感兴趣的时间点,t<sub>p</sub>, 就在桌子前面宠物.cats被丢弃了。

  4. 在已经完成的时间点恢复之外,如果还想重新执行所有语句您感兴趣的时间点,使用mysqlbinlog再次应用之后的所有事件t<sub>p</sub>到服务器。我们在步骤 2 中注意到,在我们想要跳过的语句之后,日志位于位置 355;我们可以将它用于--开始位置选项,以便包含该位置之后的任何语句:

    $> mysqlbinlog --start-position=355 /var/lib/mysql/bin.123456 \
             | mysql -u root -p
    

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