MySQL | 延迟从库 + 快照实现闪回功能

290 阅读7分钟

概述

MySQL 针对 Drop、Truncate 等 DDL 操作,只能进行备份还原。若无备份情况下,基本上数据无法找回。

某个客户误删过两次数据,第一次是误删表,第二次是误删数据库。

  • 第一次误删表恢复接近花费了5-6个小时
  • 第二次误删库,恢复花费接近 3 天。这如果是核心数据,这个恢复时间其实是有点难以接受的。

在此背景下,我们思考 MySQL 如何提高 RTO 。

下面先从技术层面分析误删的场景以及对应方式

针对无备份下的 DDL 场景,还可以使用 TwinsDB 等开源工具,直接分析数据文件进行恢复,不过此时的恢复已经无法完全保障,而且必须要保证数据文件还在文件系统缓存中,如果被覆盖或者清理掉,那就只能做备份还原。

应对如上问题,今天主要介绍 MySQL 延迟复制 + 快照实快速速数据恢复。主要恢复思路如下:

前提条件:

  • 存在一个延迟从库,延迟时间要大于已删除数据库的时间,如当前时间是 9:00,客户8:00删掉了一个库,那么延迟阈值至少要大于1个小时;

恢复思路:

  • 主库半同步降级
  • 停止延迟从库复制
  • 给延迟从库打一个虚拟机快照
  • 找到需要恢复的 GTID 或 Position
  • 基于 GTID 或 Position 将延迟从库复制追到故障点之前
  • 导出被误删的表或库,并发送给客户确认
  • 在客户确认数据无误后,做快照恢复,恢复至延迟从库停止复制那一刻
  • 正常启动延迟复制
  • 删除快照
  • 主库还原半同步设置

注意:如上操作,除了主库半同步降级以外,没有一个是在主库上进行的;所有操作均在延迟从库执行。

相比传统的 PITR,MySQL 在追日志阶段是单线程重放,使用复制模式,可以使用并行重放,所以会提高恢复效率;并行会发行只有 GTID 才支持,若基于 Position 模式,并行将失效。

# 可以并行
start slave until sql_before_gtids="e29fc82c-e336-11ed-b5ed-fefcfedd3ddb:745";

# 不可并行
start slave until master_log_file='mysql-bin.000005',master_log_pos=1181;

如果发现客户开启了 GTID,建议使用 GTID 模式做重放。

原理

延迟从库其实是放慢 SQL Thread 线程的重放,也就是说主库产生的 binlog 会源源不断的送到从库,只是从库会延迟重放这些日志。

实验

开启延迟复制

change master to 
	master_host='10.5.54.10',
	master_user='repl',
	master_password='Qwer@123',
	master_port=3306,
	master_log_file='mysql-bin.000001',
	master_log_pos=155,
	master_delay=28800;

也可以针对已运行的从库设置

stop slave;
change master to master_delay = 28800;
start slave;

延迟复制相关信息通过 show slave status\G 查看

                    SQL_Delay: 28800
          SQL_Remaining_Delay: NULL
      Slave_SQL_Running_State: Slave has read all relay log; waiting for more updates

使用延迟复制恢复误删的表

下面将使用已存在的从库设置延迟从库,来恢复误删的表;

0、数据准备

主库创建测试数据

create database test;
create table test.t1(id int primary key);
insert into test.t1 values(1),(2);
select * from test.t1;

在从库中检查数据是否有同步过来

mysql> select * from test.t1;
+----+
| id |
+----+
|  1 |
|  2 |
+----+
2 rows in set (0.00 sec)

1、DBVM 进入维护模式

2、主库半同步降级

mysql> set global rpl_semi_sync_master_timeout = 0;
Query OK, 0 rows affected (0.00 sec)

3、从库开启延迟复制

stop slave;
change master to master_delay = 28800;
start slave;

延迟 28800 秒,即 8 小时。

4、在主库中新增一条记录,并删除 t1 表。

insert into test.t1 values(3);
drop table test.t1;

此时查看从库,会发现 t1 表依然存在,且只有两条记录。

mysql> select * from test.t1;
+----+
| id |
+----+
|  1 |
|  2 |
+----+
2 rows in set (0.00 sec)

5、在主库中,查看 DROP 操作在 binlog 中的位置点信息。

mysql> show master status\G
*************************** 1. row ***************************
             File: mysql-bin.000005
         Position: 1364
     Binlog_Do_DB:
 Binlog_Ignore_DB:
Executed_Gtid_Set: 3e567c47-de8b-11ed-923a-fefcfee63601:1-88,
d7c72e02-de8a-11ed-a53c-fefcfe9ec167:1-12
1 row in set (0.00 sec)

mysql>
mysql> pager grep -iB 5 drop
PAGER set to 'grep -iB 5 drop'
mysql> show binlog events in 'mysql-bin.000005';
| mysql-bin.000005 | 1012 | Rows_query     |      1226 |        1065 | # insert into test.t1 values(3)                                                      |
| mysql-bin.000005 | 1065 | Table_map      |      1226 |        1110 | table_id: 583 (test.t1)                                                              |
| mysql-bin.000005 | 1110 | Write_rows     |      1226 |        1150 | table_id: 583 flags: STMT_END_F                                                      |
| mysql-bin.000005 | 1150 | Xid            |      1226 |        1181 | COMMIT /* xid=3583205 */                                                             |
| mysql-bin.000005 | 1181 | Gtid           |      1226 |        1246 | SET @@SESSION.GTID_NEXT= '3e567c47-de8b-11ed-923a-fefcfee63601:88'                   |
| mysql-bin.000005 | 1246 | Query          |      1226 |        1364 | DROP TABLE `test`.`t1` /* generated by server */                                     |
20 rows in set (0.00 sec)

6、将从库恢复到指定位置点

6.1 停止同步

stop slave;

6.2 给从库打个快照

6.3 将从库恢复到指定点,指定到上一个事务的 begin_pos。

# GTID 模式
start slave until sql_before_gtids="3e567c47-de8b-11ed-923a-fefcfee63601:88";

# Position 模式
change master to master_delay = 0;
start slave until master_log_file='mysql-bin.000005',master_log_pos=1181;

注意:GTID 模式下,可以指定当前 GTID,因为我们使用的是 before_gtid,他将不会执行当前的 GTID;若是 Position 模式,需要指定上一条语句的begin_positioin,也就是 Pos(第二列) 那一列。

7、从库检查执行情况

show slave status\G

确保(Relay_Master_Log_File = Until_log_File) && (Exec_Master_Log_Pos = Until_Log_Pos) && (Slave_Sql_Running = 'No')

8、查看 t1 表的内容

mysql> select * from test.t1;
+----+
| id |
+----+
|  1 |
|  2 |
|  3 |
+----+
3 rows in set (0.00 sec)

9、导出表给到客户,待客户确认

mysqldump -uroot -p -h127.0.0.1 --set-gtid-purged=OFF --default-character-set=utf8mb4 --hex-blob test t1 > test.t1.sql

10、确认无误后,延迟从库做快照还原,具体操作根据自身环境中的云能力。

11、还原延迟从库

11.1 启动数据库

systemctl restart mysqld

11.2 启动延迟复制

change master to master_delay = 28800;
start slave;

11.3 查看数据

mysql> select * from test.t1;
+----+
| id |
+----+
|  1 |
|  2 |
+----+
2 rows in set (0.08 sec)

11.4 查看复制状态

mysql> show slave status\G
*************************** 1. row ***************************
               Slave_IO_State: Waiting for master to send event
                  Master_Host: 10.5.54.9
                  Master_User: sangforroot_repl
                  Master_Port: 3306
                Connect_Retry: 60
              Master_Log_File: mysql-bin.000005
          Read_Master_Log_Pos: 1364
               Relay_Log_File: host-fefcfe9ec167-relay-bin.000002
                Relay_Log_Pos: 414
        Relay_Master_Log_File: mysql-bin.000005
             Slave_IO_Running: Yes
            Slave_SQL_Running: Yes
              Replicate_Do_DB:
          Replicate_Ignore_DB:
           Replicate_Do_Table:
       Replicate_Ignore_Table:
      Replicate_Wild_Do_Table:
  Replicate_Wild_Ignore_Table:
                   Last_Errno: 0
                   Last_Error:
                 Skip_Counter: 0
          Exec_Master_Log_Pos: 879
              Relay_Log_Space: 1118
              Until_Condition: None
               Until_Log_File:
                Until_Log_Pos: 0
           Master_SSL_Allowed: No
           Master_SSL_CA_File:
           Master_SSL_CA_Path:
              Master_SSL_Cert:
            Master_SSL_Cipher:
               Master_SSL_Key:
        Seconds_Behind_Master: 1898
Master_SSL_Verify_Server_Cert: No
                Last_IO_Errno: 0
                Last_IO_Error:
               Last_SQL_Errno: 0
               Last_SQL_Error:
  Replicate_Ignore_Server_Ids:
             Master_Server_Id: 1226
                  Master_UUID: 3e567c47-de8b-11ed-923a-fefcfee63601
             Master_Info_File: mysql.slave_master_info
                    SQL_Delay: 28800
          SQL_Remaining_Delay: 26902
      Slave_SQL_Running_State: Waiting until MASTER_DELAY seconds after master executed event
           Master_Retry_Count: 86400
                  Master_Bind:
      Last_IO_Error_Timestamp:
     Last_SQL_Error_Timestamp:
               Master_SSL_Crl:
           Master_SSL_Crlpath:
           Retrieved_Gtid_Set: 3e567c47-de8b-11ed-923a-fefcfee63601:87-88
            Executed_Gtid_Set: 3e567c47-de8b-11ed-923a-fefcfee63601:1-86,
d7c72e02-de8a-11ed-a53c-fefcfe9ec167:1-12
                Auto_Position: 1
         Replicate_Rewrite_DB:
                 Channel_Name:
           Master_TLS_Version:
1 row in set (0.00 sec)

12、删除快照

13、主库还原半同步设置