数据库难免会被误操作,比如delete,drop等有害的语句被执行后,该如何恢复对dba是一个考验,如果备份可用的话,可以从备份中恢复数据来,不过这样做一般的成本有些大。
binlog2sql工具是大众点评团队用python工具写的一个工具,通过解析binlog的sql,将sql语句反解析达到恢复数据的目的。如果没有这个工具,当然我们可以通过复杂的sed命令来手动生成恢复的语句,不过binglog2sql将这一步操作做了简便处理。
这篇blog是通过手动生成恢复语句:http://www.cnblogs.com/gomysql/p/3582058.html
binlog2sql官方文档:https://github.com/danfengcao/binlog2sql
下面进行安装和试用:
1.先配置好yum后安装git,yum的配置参考:https://www.cnblogs.com/yizhichun/p/6339742.html
2.安装binlogs2sql之前的准备工作
首先需要安装好git和pip工具
git的安装比较简单,网上有很多资料
[root@qht131 yum.repos.d]# yum install git-core
不过在安装pip时出现了各种问题,好在最后安装成功了。
官方安装文档:https://pip.pypa.io/en/stable/installing/
安装pip之前需要先升级python文档:https://blog.csdn.net/see_you_see_me/article/details/78550977
如果安装过程中出错了处理方法参考:http://www.zhimengzhe.com/linux/251710.html
[root@qht131 binlog2sql]# python -V
Python 2.7.14
[root@qht131 binlog2sql]# git --version
git version 1.7.1
[root@qht131 binlog2sql]# pip -V
pip 10.0.1 from /usr/local/python2.7/lib/python2.7/site-packages/pip (python 2.7)
3.安装binglog2sql工具
shell> git clone https://github.com/danfengcao/binlog2sql.git && cd binlog2sql
我在用git下载原文件的时候出错,通过安装curl及curl-devel来解决的
yum install curl curl-devel
[root@qht131 binlog2sql]# pip install -r requirements.txt
4.设置参数及帐户权限
[root@qht131 binlog2sql]# cat /etc/my.cnf
...
server-id=10000
log_bin = /u01/mysql/mysql_bin
max_binlog_size=1G
binlog_format = row
binlog_row_image = full
mysql> create user btwos@'localhost' identified by '123456';
Query OK, 0 rows affected (0.06 sec)
mysql> GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* to btwos@localhost;
Query OK, 0 rows affected (0.00 sec)
准备测试数据
mysql> create table test (
-> id int not null auto_increment,
-> name varchar(20) not null,
-> create_time datetime not null,
-> primary key (id));
Query OK, 0 rows affected (0.07 sec)
mysql> insert into test(name,create_time) values ('Xiaolin','2015-10-1'),('WangMac','2016-08-25'),('Jackli','2017-04-10');
Query OK, 3 rows affected (0.21 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from test;
+----+---------+---------------------+
| id | name | create_time |
+----+---------+---------------------+
| 1 | Xiaolin | 2015-10-01 00:00:00 |
| 2 | WangMac | 2016-08-25 00:00:00 |
| 3 | Jackli | 2017-04-10 00:00:00 |
+----+---------+---------------------+
3 rows in set (0.02 sec)
5.测试delete以及update操作
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql_bin.000040 | 1212 | | | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.01 sec)
mysql> update test set create_time='2016-10-10' where name='WangMac';
Query OK, 1 row affected (0.03 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> delete from test where name='Jackli';
Query OK, 1 row affected (0.00 sec)
mysql> commit;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from test;
+----+---------+---------------------+
| id | name | create_time |
+----+---------+---------------------+
| 1 | Xiaolin | 2015-10-01 00:00:00 |
| 2 | WangMac | 2016-10-10 00:00:00 |
+----+---------+---------------------+
2 rows in set (0.00 sec)
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql_bin.000040 | 1772 | | | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
修改了一笔数据,以及删除了最后一笔数据。
先查看一下mysqlbinlog解析出来的日志,从日志可以看到从建表到update和delete的所有操作。
[root@qht131 mysql]# mysqlbinlog --no-defaults -v -v mysql_bin.000040 > 000040.log
[root@qht131 mysql]# cat 000040.log
# at 706
#180426 11:01:35 server id 10000 end_log_pos 906 CRC32 0x6f60578e Query thread_id=3 exec_time=0 error_code=0
use `l5m`/*!*/;
SET TIMESTAMP=1524711695/*!*/;
create table test (
id int not null auto_increment,
name varchar(20) not null,
create_time datetime not null,
primary key (id))
/*!*/;
# at 906
#180426 11:03:43 server id 10000 end_log_pos 971 CRC32 0x91159a2a Anonymous_GTIDlast_committed=3 sequence_number=4 rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 971
#180426 11:03:43 server id 10000 end_log_pos 1042 CRC32 0xb26843e4 Query thread_id=3 exec_time=0 error_code=0
SET TIMESTAMP=1524711823/*!*/;
BEGIN
/*!*/;
# at 1042
#180426 11:03:43 server id 10000 end_log_pos 1093 CRC32 0x72c273fe Table_map: `l5m`.`test` mapped to number 108
# at 1093
#180426 11:03:43 server id 10000 end_log_pos 1181 CRC32 0xb0a6183b Write_rows: table id 108 flags: STMT_END_F
BINLOG '
j0HhWhMQJwAAMwAAAEUEAAAAAGwAAAAAAAEAA2w1bQAEdGVzdAADAw8SAzwAAAD+c8Jy
j0HhWh4QJwAAWAAAAJ0EAAAAAGwAAAAAAAEAAgAD//gBAAAAB1hpYW9saW6Zl0IAAPgCAAAAB1dh
bmdNYWOZmjIAAPgDAAAABkphY2tsaZmcVAAAOximsA==
'/*!*/;
### INSERT INTO `l5m`.`test`
### SET
### @1=1 /* INT meta=0 nullable=0 is_null=0 */
### @2='Xiaolin' /* VARSTRING(60) meta=60 nullable=0 is_null=0 */
### @3='2015-10-01 00:00:00' /* DATETIME(0) meta=0 nullable=0 is_null=0 */
### INSERT INTO `l5m`.`test`
### SET
### @1=2 /* INT meta=0 nullable=0 is_null=0 */
### @2='WangMac' /* VARSTRING(60) meta=60 nullable=0 is_null=0 */
### @3='2016-08-25 00:00:00' /* DATETIME(0) meta=0 nullable=0 is_null=0 */
### INSERT INTO `l5m`.`test`
### SET
### @1=3 /* INT meta=0 nullable=0 is_null=0 */
### @2='Jackli' /* VARSTRING(60) meta=60 nullable=0 is_null=0 */
### @3='2017-04-10 00:00:00' /* DATETIME(0) meta=0 nullable=0 is_null=0 */
# at 1181
#180426 11:03:43 server id 10000 end_log_pos 1212 CRC32 0x9e3baa9e Xid = 21
COMMIT/*!*/;
# at 1212
#180426 11:18:57 server id 10000 end_log_pos 1277 CRC32 0x7d82517c Anonymous_GTIDlast_committed=4 sequence_number=5 rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 1277
#180426 11:18:57 server id 10000 end_log_pos 1348 CRC32 0x0ebea2ac Query thread_id=3 exec_time=0 error_code=0
SET TIMESTAMP=1524712737/*!*/;
BEGIN
/*!*/;
# at 1348
#180426 11:18:57 server id 10000 end_log_pos 1399 CRC32 0x86e375ff Table_map: `l5m`.`test` mapped to number 108
# at 1399
#180426 11:18:57 server id 10000 end_log_pos 1471 CRC32 0xfb2db159 Update_rows: table id 108 flags: STMT_END_F
BINLOG '
IUXhWhMQJwAAMwAAAHcFAAAAAGwAAAAAAAEAA2w1bQAEdGVzdAADAw8SAzwAAAD/deOG
IUXhWh8QJwAASAAAAL8FAAAAAGwAAAAAAAEAAgAD///4AgAAAAdXYW5nTWFjmZoyAAD4AgAAAAdX
YW5nTWFjmZqUAABZsS37
'/*!*/;
### UPDATE `l5m`.`test`
### WHERE
### @1=2 /* INT meta=0 nullable=0 is_null=0 */
### @2='WangMac' /* VARSTRING(60) meta=60 nullable=0 is_null=0 */
### @3='2016-08-25 00:00:00' /* DATETIME(0) meta=0 nullable=0 is_null=0 */
### SET
### @1=2 /* INT meta=0 nullable=0 is_null=0 */
### @2='WangMac' /* VARSTRING(60) meta=60 nullable=0 is_null=0 */
### @3='2016-10-10 00:00:00' /* DATETIME(0) meta=0 nullable=0 is_null=0 */
# at 1471
#180426 11:18:57 server id 10000 end_log_pos 1502 CRC32 0x0f213682 Xid = 25
COMMIT/*!*/;
# at 1502
#180426 11:19:11 server id 10000 end_log_pos 1567 CRC32 0x15dd429a Anonymous_GTIDlast_committed=5 sequence_number=6 rbr_only=yes
/*!50718 SET TRANSACTION ISOLATION LEVEL READ COMMITTED*//*!*/;
SET @@SESSION.GTID_NEXT= 'ANONYMOUS'/*!*/;
# at 1567
#180426 11:19:11 server id 10000 end_log_pos 1638 CRC32 0x926675d7 Query thread_id=3 exec_time=0 error_code=0
SET TIMESTAMP=1524712751/*!*/;
BEGIN
/*!*/;
# at 1638
#180426 11:19:11 server id 10000 end_log_pos 1689 CRC32 0x83a25989 Table_map: `l5m`.`test` mapped to number 108
# at 1689
#180426 11:19:11 server id 10000 end_log_pos 1741 CRC32 0xbb2e56b2 Delete_rows: table id 108 flags: STMT_END_F
BINLOG '
L0XhWhMQJwAAMwAAAJkGAAAAAGwAAAAAAAEAA2w1bQAEdGVzdAADAw8SAzwAAACJWaKD
L0XhWiAQJwAANAAAAM0GAAAAAGwAAAAAAAEAAgAD//gDAAAABkphY2tsaZmcVAAAslYuuw==
'/*!*/;
### DELETE FROM `l5m`.`test`
### WHERE
### @1=3 /* INT meta=0 nullable=0 is_null=0 */
### @2='Jackli' /* VARSTRING(60) meta=60 nullable=0 is_null=0 */
### @3='2017-04-10 00:00:00' /* DATETIME(0) meta=0 nullable=0 is_null=0 */
下面使用binlog2sql来解析:
[root@qht131 binlog2sql]# python binlog2sql.py -h127.0.0.1 -P3306 -ubtwos -p '123456' -dl5m -ttest --start-file='mysql_bin.000040'
CREATE USER 'btwos'@'localhost' IDENTIFIED WITH 'mysql_native_password' AS '*6BB4837EB74329105EE4568DDA7DC67ED2CA2AD9';
GRANT SELECT, REPLICATION SLAVE, REPLICATION CLIENT ON *.* TO 'btwos'@'localhost';
USE l5m;
create table test (
id int not null auto_increment,
name varchar(20) not null,
create_time datetime not null,
primary key (id));
INSERT INTO `l5m`.`test`(`create_time`, `id`, `name`) VALUES ('2015-10-01 00:00:00', 1, 'Xiaolin'); #start 906 end 1181 time 2018-04-26 11:03:43
INSERT INTO `l5m`.`test`(`create_time`, `id`, `name`) VALUES ('2016-08-25 00:00:00', 2, 'WangMac'); #start 906 end 1181 time 2018-04-26 11:03:43
INSERT INTO `l5m`.`test`(`create_time`, `id`, `name`) VALUES ('2017-04-10 00:00:00', 3, 'Jackli'); #start 906 end 1181 time 2018-04-26 11:03:43
UPDATE `l5m`.`test` SET `create_time`='2016-10-10 00:00:00', `id`=2, `name`='WangMac' WHERE `create_time`='2016-08-25 00:00:00' AND `id`=2 AND `name`='WangMac' LIMIT 1; #start 1212 end 1471 time 2018-04-26 11:18:57
DELETE FROM `l5m`.`test` WHERE `create_time`='2017-04-10 00:00:00' AND `id`=3 AND `name`='Jackli' LIMIT 1; #start 1502 end 1741 time 2018-04-26 11:19:11
完美呈现了所有以test的操作,并且还备注了在binlog中的position以及执行的时间
下面重点来了,解析出回滚的sql
[root@qht131 binlog2sql]# python binlog2sql.py --flashback -h127.0.0.1 -P3306 -ubtwos -p '123456' -dl5m -ttest --start-file='mysql_bin.000040'
INSERT INTO `l5m`.`test`(`create_time`, `id`, `name`) VALUES ('2017-04-10 00:00:00', 3, 'Jackli'); #start 1502 end 1741 time 2018-04-26 11:19:11
UPDATE `l5m`.`test` SET `create_time`='2016-08-25 00:00:00', `id`=2, `name`='WangMac' WHERE `create_time`='2016-10-10 00:00:00' AND `id`=2 AND `name`='WangMac' LIMIT 1; #start 1212 end 1471 time 2018-04-26 11:18:57
DELETE FROM `l5m`.`test` WHERE `create_time`='2017-04-10 00:00:00' AND `id`=3 AND `name`='Jackli' LIMIT 1; #start 906 end 1181 time 2018-04-26 11:03:43
DELETE FROM `l5m`.`test` WHERE `create_time`='2016-08-25 00:00:00' AND `id`=2 AND `name`='WangMac' LIMIT 1; #start 906 end 1181 time 2018-04-26 11:03:43
DELETE FROM `l5m`.`test` WHERE `create_time`='2015-10-01 00:00:00' AND `id`=1 AND `name`='Xiaolin' LIMIT 1; #start 906 end 1181 time 2018-04-26 11:03:43
解析出来的sql是根据时间戳倒序的,delete语句被flashback成了insert语句,update语句也可以成功将数据恢复到之前的状态,insert语句被flashback成了delete语句。
如果需要恢复数据的话只需要直接执行这些语句就可以了。
6.测试delete,truncate以及drop操作
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 |
+------------------+----------+--------------+------------------+-------------------+
| mysql_bin.000041 | 154 | | | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
mysql> use l5m
Database changed
mysql> delete from test where id<3;
Query OK, 2 rows affected (0.03 sec)
mysql> select * from test;
Empty set (0.00 sec)
mysql> insert into test (name,create_time) values('ChunkA','2019-10-08');
Query OK, 1 row affected (0.00 sec)
mysql> select * from test;
+----+--------+---------------------+
| id | name | create_time |
+----+--------+---------------------+
| 4 | ChunkA | 2019-10-08 00:00:00 |
+----+--------+---------------------+
1 row in set (0.00 sec)
mysql> truncate table test;
Query OK, 0 rows affected (0.01 sec)
mysql> select * from test;
Empty set (0.00 sec)
mysql> drop table test;
Query OK, 0 rows affected (0.02 sec)
mysql> show master status;
+------------------+----------+--------------+------------------+-------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB | Executed_Gtid_Set |
+------------------+----------+--------------+------------------+-------------------+
| mysql_bin.000041 | 1043 | | | |
+------------------+----------+--------------+------------------+-------------------+
1 row in set (0.00 sec)
直接用binlog2sql来解析binlog
[root@qht131 binlog2sql]# python binlog2sql.py -h127.0.0.1 -P3306 -ubtwos -p '123456' -dl5m -ttest --start-file='mysql_bin.000041'
USE l5m;
truncate table test;
USE l5m;
DROP TABLE `test` /* generated by server */;
发现解析不出来truncate之前delete和insert操作,应该是由于information_schema.tables已没有test表的信息,导致解析不出来。
那如果我重新把test表建立好之后再用binlog2sql来解析行不行?
mysql> create table test (
-> id int not null auto_increment,
-> name varchar(20) not null,
-> create_time datetime not null,
-> primary key (id));
Query OK, 0 rows affected (0.03 sec)
[root@qht131 binlog2sql]# python binlog2sql.py -h127.0.0.1 -P3306 -ubtwos -p '123456' -dl5m -ttest --start-file='mysql_bin.000041'
DELETE FROM `l5m`.`test` WHERE `create_time`='2015-10-01 00:00:00' AND `id`=1 AND `name`='Xiaolin' LIMIT 1; #start 4 end 412 time 2018-04-26 11:55:27
DELETE FROM `l5m`.`test` WHERE `create_time`='2016-10-10 00:00:00' AND `id`=2 AND `name`='WangMac' LIMIT 1; #start 4 end 412 time 2018-04-26 11:55:27
INSERT INTO `l5m`.`test`(`create_time`, `id`, `name`) VALUES ('2019-10-08 00:00:00', 4, 'ChunkA'); #start 443 end 682 time 2018-04-26 11:56:10
USE l5m;
truncate table test;
USE l5m;
DROP TABLE `test` /* generated by server */;
USE l5m;
create table test (
id int not null auto_increment,
name varchar(20) not null,
create_time datetime not null,
primary key (id));
没有问题,binlog2sql倒序着把binlog的所有操作都解析出来了。
接着就好flashback了生成回滚sql了:
[root@qht131 binlog2sql]# python binlog2sql.py --flashback -h127.0.0.1 -P3306 -ubtwos -p '123456' -dl5m -ttest --start-file='mysql_bin.000041'
DELETE FROM `l5m`.`test` WHERE `create_time`='2019-10-08 00:00:00' AND `id`=4 AND `name`='ChunkA' LIMIT 1; #start 443 end 682 time 2018-04-26 11:56:10
INSERT INTO `l5m`.`test`(`create_time`, `id`, `name`) VALUES ('2016-10-10 00:00:00', 2, 'WangMac'); #start 4 end 412 time 2018-04-26 11:55:27
INSERT INTO `l5m`.`test`(`create_time`, `id`, `name`) VALUES ('2015-10-01 00:00:00', 1, 'Xiaolin'); #start 4 end 412 time 2018-04-26 11:55:27
可以看到,此时批量 delete 和 insert 的语句被解析出来了,并且能够成功生成回滚的 SQL。
参考:https://www.cnblogs.com/glon/p/6856192.html
https://github.com/danfengcao/binlog2sql