1.
如果想通过 mysql 的 binlog 恢复数据,首先要开启 binlog 。这里搭建一个测试的环境,了解一下 mysql binlog 是如何恢复数据库的。原理比较简单,binlog 会存储mysql中变化的数据,比如你创建了一个数据库,写入了一些数据,这些都会存储在 mysql 的 binlog 中。
需要恢复的时候就找到,两个位置,一个起始位置,一个结束的位置。结束的位置,一半是数据被破坏或者删除前的位置。mysql 8 默认已经开启了 binlog
mysql> show variables like '%log_bin%';
+---------------------------------+---------------------------------+
| Variable_name | Value |
+---------------------------------+---------------------------------+
| log_bin | ON |
| log_bin_basename | /var/lib/mysql/master-bin |
| log_bin_index | /var/lib/mysql/master-bin.index |
| log_bin_trust_function_creators | OFF |
| log_bin_use_v1_row_events | OFF |
| sql_log_bin | ON |
+---------------------------------+---------------------------------+
6 rows in set (0.01 sec)
2 . 执行重置(reset master)后 ,可以看到之前的 binlog 文件已经被删除了,产生一个新的 binlog 文件。
可以查看一下这个文件的内容
mysql> show master logs;
+-------------------+-----------+
| Log_name | File_size |
+-------------------+-----------+
| master-bin.000001 | 962 |
| master-bin.000002 | 242 |
| master-bin.000003 | 242 |
| master-bin.000004 | 242 |
| master-bin.000005 | 401 |
| master-bin.000006 | 194 |
+-------------------+-----------+
6 rows in set (0.00 sec)
mysql> show master logs;
+-------------------+-----------+
| Log_name | File_size |
+-------------------+-----------+
| master-bin.000001 | 962 |
| master-bin.000002 | 242 |
| master-bin.000003 | 242 |
| master-bin.000004 | 242 |
| master-bin.000005 | 401 |
| master-bin.000006 | 194 |
+-------------------+-----------+
6 rows in set (0.00 sec)
mysql> reset master;
Query OK, 0 rows affected (0.01 sec)
mysql> show master logs;
+-------------------+-----------+
| Log_name | File_size |
+-------------------+-----------+
| master-bin.000001 | 154 |
+-------------------+-----------+
1 row in set (0.00 sec)
可以查看一下这个文件的内容
[root@localhost mysql]# ls -al
-rw-r----- 1 mysql mysql 154 8月 8 16:07 master-bin.000001
-rw-r----- 1 mysql mysql 20 8月 8 16:07 master-bin.index
[root@localhost mysql]# mysqlbinlog master-bin.000001
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
# at 4
#220808 16:07:41 server id 1 end_log_pos 123 CRC32 0xadec6205 Start: binlog v 4, server v 5.7.35-log created 220808 16:07:41 at startup
# Warning: this binlog is either in use or was not closed properly.
ROLLBACK/*!*/;
BINLOG '
TcTwYg8BAAAAdwAAAHsAAAABAAQANS43LjM1LWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAABNxPBiEzgNAAgAEgAEBAQEEgAAXwAEGggAAAAICAgCAAAACgoKKioAEjQA
AQVi7K0=
'/*!*/;
# at 123
#220808 16:07:41 server id 1 end_log_pos 154 CRC32 0x5c15c4eb Previous-GTIDs
# [empty]
SET @@SESSION.GTID_NEXT= 'AUTOMATIC' /* added by mysqlbinlog */ /*!*/;
DELIMITER ;
# End of log file
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;
这个时候,我们添加一些数据
mysql> create database test;
Query OK, 1 row affected (0.00 sec)
mysql> use test;
Database changed
mysql> create table jettech01(id int,name char);
Query OK, 0 rows affected (0.01 sec)
mysql> insert into jettech01 value(3,'c');
Query OK, 1 row affected (0.00 sec)
mysql> flush privileges;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from jettech01;
+------+------+
| id | name |
+------+------+
| 3 | c |
+------+------+
1 row in set (0.00 sec)
这个时候,不小心删除了,数据库 test
mysql> drop database test;
Query OK, 1 row affected (0.01 sec)
binlog 大小没变还是没删除之前的数据都在里面
[root@localhost mysql]# ls -al
-rw-r----- 1 mysql mysql 913 8月 8 16:10 master-bin.000001
-rw-r----- 1 mysql mysql 20 8月 8 16:09 master-bin.index
3 现在就需要解决一个实际问题,怎么恢复这个数据库
首先为了防止干扰,执行 flush logs ,产生一个新binlog 文件
mysql> show master logs;
+-------------------+-----------+
| Log_name | File_size |
+-------------------+-----------+
| master-bin.000001 | 1070 |
+-------------------+-----------+
1 row in set (0.00 sec)
mysql> show master status;
+-------------------+----------+--------------+-------------------------------------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+-------------------------------------------------+-------------------+
| master-bin.000001 | 1070 | test,test1 | mysql,performance_schema,information_schema,sys | |
+-------------------+----------+--------------+-------------------------------------------------+-------------------+
1 row in set (0.00 sec)
mysql> flush logs;
Query OK, 0 rows affected (0.01 sec)
mysql> show master status;
+-------------------+----------+--------------+-------------------------------------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+-------------------+----------+--------------+-------------------------------------------------+-------------------+
| master-bin.000002 | 154 | test,test1 | mysql,performance_schema,information_schema,sys | |
+-------------------+----------+--------------+-------------------------------------------------+-------------------+
1 row in set (0.00 sec)
系统文件:
[root@localhost mysql]# cat master-bin.index
./master-bin.000001
./master-bin.000002
[root@localhost mysql]# ls -al master-bin.*
-rw-r----- 1 mysql mysql 1118 8月 8 16:12 master-bin.000001
-rw-r----- 1 mysql mysql 154 8月 8 16:12 master-bin.000002
-rw-r----- 1 mysql mysql 40 8月 8 16:12 master-bin.index
4.恢复数据,首先要找到数据在哪里被删除了。
[root@localhost mysql]# mysqlbinlog master-bin.000001 | grep -n "drop database"
76:drop database test
可以看到在 76 行的地方有个 删除语句。终可以找到两个地方
- 数据需要恢复的起始位置
- 数据需要恢复的结束位置
这里起始的位置就找 创建数据库的位置,结束的位置就找 删除数据库的位置。
# at 219
#220808 16:09:45 server id 1 end_log_pos 313 CRC32 0x781d4308 Query thread_id=6 exec_time=0 error_code=0
SET TIMESTAMP=1659946185/*!*/;
SET @@session.pseudo_thread_id=6/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=1436549152/*!*/;
SET @@session.auto_increment_increment=1, @@session.auto_increment_offset=1/*!*/;
/*!\C utf8 *//*!*/;
SET @@session.character_set_client=33,@@session.collation_connection=33,@@session.collation_server=8/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
create database test
/*!*/;
36 # at 313
37 #220808 16:09:54 server id 1 end_log_pos 378 CRC32 0xed8705a1 Anonymous_GTID last_committed=1 sequence_number=2 rbr_only=no
38 SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
39 # at 378
40 #220808 16:09:54 server id 1 end_log_pos 492 CRC32 0x372bd559 Query thread_id=6 exec_time=0 error_code=0
41 use `test`/*!*/;
42 SET TIMESTAMP=1659946194/*!*/;
43 create table jettech01(id int,name char)
44 /*!*/;
45 # at 492
46 #220808 16:09:59 server id 1 end_log_pos 557 CRC32 0xf2b51311 Anonymous_GTID last_committed=2 sequence_number=3 rbr_only=yes
47 /*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
48 SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
49 # at 557
50 #220808 16:09:59 server id 1 end_log_pos 629 CRC32 0x5755f64f Query thread_id=6 exec_time=0 error_code=0
51 SET TIMESTAMP=1659946199/*!*/;
52 BEGIN
53 /*!*/;
54 # at 629
55 #220808 16:09:59 server id 1 end_log_pos 684 CRC32 0xc59d8d91 Table_map: `test`.`jettech01` mapped to number 113
56 # at 684
57 #220808 16:09:59 server id 1 end_log_pos 726 CRC32 0x9b8b6300 Write_rows: table id 113 flags: STMT_END_F
58
59 BINLOG '
60 18TwYhMBAAAANwAAAKwCAAAAAHEAAAAAAAEABHRlc3QACWpldHRlY2gwMQACA/4C/gEDkY2dxQ==
61 18TwYh4BAAAAKgAAANYCAAAAAHEAAAAAAAEAAgAC//wDAAAAAWMAY4ub
62 '/*!*/;
63 # at 726
64 #220808 16:09:59 server id 1 end_log_pos 757 CRC32 0x86c37763 Xid = 399
65 COMMIT/*!*/;
66 # at 757
67 #220808 16:10:10 server id 1 end_log_pos 822 CRC32 0xda41b64a Anonymous_GTID last_committed=3 sequence_number=4 rbr_only=no
68 SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
69 # at 822
70 #220808 16:10:10 server id 1 end_log_pos 913 CRC32 0xc428bae4 Query thread_id=6 exec_time=0 error_code=0
71 SET TIMESTAMP=1659946210/*!*/;
72 SET @@session.time_zone='SYSTEM'/*!*/;
73 flush privileges
74 /*!*/;
75 # at 913
76 #220808 16:11:05 server id 1 end_log_pos 978 CRC32 0x4ee77a13 Anonymous_GTID last_committed=4 sequence_number=5 rbr_only=no
77 SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
78 # at 978
79 #220808 16:11:05 server id 1 end_log_pos 1070 CRC32 0x0ec87860 Query thread_id=6 exec_time=0 error_code=0
80 SET TIMESTAMP=1659946265/*!*/;
81 drop database test
82 /*!*/;
83 # at 1070
start_position:219
end_position:978
恢复数据报错:
[root@localhost mysql]# mysqlbinlog -vv master-bin.000001 --start-position=219 --stop-position=978 | mysql -uroot -p
Enter password:
ERROR 1782 (HY000) at line 23: @@SESSION.GTID_NEXT cannot be set to ANONYMOUS when @@GLOBAL.GTID_MODE = ON.
解决方案
MySQL :: MySQL 5.7 Reference Manual :: 16.1.4.3 Disabling GTID Transactions Online
mysql> show global variables like 'gtid_mode';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| gtid_mode | ON |
+---------------+-------+
1 row in set (0.01 sec)
mysql> set @@GLOBAL.GTID_MODE = ON_PERMISSIVE;
Query OK, 0 rows affected (0.00 sec)
mysql> set @@GLOBAL.GTID_MODE = OFF_PERMISSIVE;
Query OK, 0 rows affected (0.00 sec)
mysql> show global variables like 'gtid_mode';
+---------------+----------------+
| Variable_name | Value |
+---------------+----------------+
| gtid_mode | OFF_PERMISSIVE |
+---------------+----------------+
1 row in set (0.00 sec)
注:更改 GTID_MODE 状态顺序为 ON<->ON_PERMISSIVE<->OFF_PERMISSIVE<->OFF ,需要按照顺序依次改变。
再次执行就不会报错了:
[root@localhost mysql]# mysqlbinlog -vv master-bin.000001 --start-position=219 --stop-position=978 | mysql -uroot -p
Enter password:
[root@localhost mysql]
检查数据
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| sys |
| test |
+--------------------+
5 rows in set (0.00 sec)
mysql> select * from test.jettech01;
+------+------+
| id | name |
+------+------+
| 3 | c |
+------+------+
1 row in set (0.00 sec)
在恢复回去GTID_MODE
mysql> set @@GLOBAL.GTID_MODE = ON;
ERROR 1788 (HY000): The value of @@GLOBAL.GTID_MODE can only be changed one step at a time: OFF <-> OFF_PERMISSIVE <-> ON_PERMISSIVE <-> ON. Also note that this value must be stepped up or down simultaneously on all servers. See the Manual for instructions.
mysql> set @@GLOBAL.GTID_MODE = ON_PERMISSIVE;
Query OK, 0 rows affected (0.00 sec)
mysql> set @@GLOBAL.GTID_MODE = ON;
Query OK, 0 rows affected (0.00 sec)
mysql> show global variables like 'gtid_mode';
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| gtid_mode | ON |
+---------------+-------+
1 row in set (0.01 sec)