mysql binlog数据恢复

今天我给大家分享一下binlog数据恢复的几种方法。如果你是数据库管理员,相信通过这节课,可以帮助你快速进行数据恢复。当然其他职业的同学,也能一定程度上了解到数据的价值,以及恢复的套路。

要谈数据恢复问题,先不得不说一下数据的重要性。可以说,一个企业最核心的就是数据了,我一个老师曾说,不管你什么架构,一切都是为数据服务的,数据是一个企业最核心的价值。不过出现问题的话,数据一旦被删除,每过一秒,都将对企业造成巨大的损失。最近,还真的发生了一起删库跑路事件,微盟7*24小时紧急恢复数据,商家赔付1.5亿。(图1)
mysql binlog数据恢复
想想,如果我们能够快速对数据进行恢复,那不就可以把损失降低,甚至到最低。我们优先要做的,就是找到一种可以快速恢复数据的方式,将恢复时间尽可能缩短。

这里我们只拿MySQL数据库来举例,binlog是二进制日志,用于记录用户对数据库操作的SQL语句。各个从库的同步,也是依靠binlog日志,它是非常重要的日志。其他关系型数据库,也大同小异,利用Oracle数据库,我们可以利用归档日志。

那么,我们该如何进行数据恢复呢?我认为,这还得看你是怎么备份的。接下来我们就简单讨论下主流的备份方案。

一个是全量备份,它适用量级较小的库,全量恢复快。举个例子,每天0点我们进行全量备份,如果我们想恢复7号0点之前的数据,毫无疑问,直接把7号的备份的全量恢复就行了。不过有一种情况是,如果我们想恢复7号0:00~23:59的数据,该怎么办呢?那么就只能利用binlog来进行恢复了。(图2)
mysql binlog数据恢复
还有一种主流的备份方案是全量+增量备份,这种方案适用于量级较大的库,节约磁盘空间,全量恢复慢。举个例子来说,每月1号0点,我们进行一次全量备份,后续每天0点进行增量备份。如果我们想恢复7号0点之前的数据,可以看出,只需要先恢复1号的全量数据,加上2号~7号的增量数据就可以了。不过有一种情况是,如果我们想恢复7号0:00~23:59的数据呢?那么就只能利用binlog来进行恢复了。(图3)
mysql binlog数据恢复
今天的分享里,我们暂时不去讨论全量,或者增量的恢复数据的效率问题,主要讲一讲如何利用binlog进行恢复。如果想利用binlog,我认为你不得不了解下binlog的三种模式,这里我们以5.7.21版本为例,注意:5.7.7之前的版本,默认格式为Statement Level,之后默认为Row Level。

先来看下Statement Level模式,它是基于SQL语句的复制,只会将SQL语句记录到binlog中。因为这种模式只存储SQL,没有真正的数据,所以无法进行数据恢复,生产环境一般不用这种模式。

再来看下Row Level模式,它是基于行的复制,并且会将每一条数据的变化记录到binlog中,是没有SQL语句的。这种我们可以解析出真正的数据,生产中建议采用Row Level模式,当然它也有一些缺点,比如记录数据时产生大量binlog,面对存储过程可能出现不一致问题。

要说的第三种模式是Mixed模式,它是混合模式复制,默认格式是Statement,某些情况下会切换到Row格式。

说完了模式,模式该如何进行查询呢?可以通过binlog_format参数来获取。(code1)

mysql> show variables like ‘binlog_format‘;
+---------------+-------+
| Variable_name | Value |
+---------------+-------+
| binlog_format | ROW   |
+---------------+-------+
1 row in set (0.00 sec)

我们使用的是MySQL 5.7.21版本的数据库,这里面的参数是经我单独修改过的,一般线上系统,也是采用的这种模式。(code1)

OK,搞清楚了这些基础知识后,接下来我们就可以对binlog进行解析与恢复了,一起来看下。

首先来看解析,那怎么理解它呢?它的意思就是将二进制binlog格式,解析成SQL语句的形式,这些SQL是数据库操作的正向结果。我们先来建立一些基础数据。(code2)

(test) 14:23:31> CREATE TABLE `t1` (
    ->   `id` int NOT NULL AUTO_INCREMENT,
    ->   `name` varchar(60) DEFAULT NULL,
    ->   PRIMARY KEY (`id`)
    -> ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.02 sec)

(test) 14:23:31> insert into t1(name) values(‘ljp1‘),(‘ljp2‘);
Query OK, 2 rows affected (0.00 sec)
Records: 2  Duplicates: 0  Warnings: 0

(test) 14:23:31> select * from t1;
+----+------+
| id | name |
+----+------+
|  1 | ljp1 |
|  2 | ljp2 |
+----+------+
2 rows in set (0.00 sec)

这里我们建立了一张表t1,它里面包含两个字段,并插入两行数据。接下来,我们对它进行解析,可以看到,时间为:14:23:31 时刻插入了两行数据。(code2)

我们根据时间找到对应的binlog,找到包含14:23:31的binlog日志,就可以看到是mysql-bin.000009文件。(code3)

[ binlog]# ll mysql-bin.000009
-rw-r----- 1 mysql mysql  837 Mar  2 14:23 mysql-bin.000009

这个文件是二进制文件,我们可以利用MySQL 自带工具mysqlbinlog,来进行解析,看看具体内容,是否与我们写入的信息一致。(code4)

[ binlog]# /usr/local/mysql/bin/mysqlbinlog --base64-output=decode-rows -v mysql-bin.000009
......
# at 259
......
CREATE TABLE `t1` (
  `id` int NOT NULL AUTO_INCREMENT,
  `name` varchar(60) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8
......
### INSERT INTO `test`.`t1`
### SET
###   @1=1
###   @2=‘ljp1‘
### INSERT INTO `test`.`t1`
### SET
###   @1=2
###   @2=‘ljp2‘
COMMIT/*!*/;
......

可以看到,利用MySQL自带工具mysqlbinlog,我们就可以轻松对binlog进行解析了,部分无关内容我已经删除,正向解析后得到的SQL,与我们先前执行的SQL一致,并且时间点对应也是一致的。如果你只是想看某人到底在数据里修改了什么,通过这个工具,就可以轻松搞定了。(code4)

那么问题来了,我们的目的是为了恢复数据,比如,将前面insertSQL解析成deleteSQL语句,因为insert可能是误插入数据。如果你数据量较少,我们可以利用前面的方式进行正向解析,手动修改成deleteSQL就可以。

但是,如果生产系统,binlog设置为2GB,解析后SQL可能有上千万行,这个量级我们手动修改短时间内是无法完成的。那么有没有一种可以直接生成反向SQL语句的方式呢?答案是有的,这样我们就可以 直接将insertSQL解析成deleteSQL了,从而实现数据的快速恢复。为了实现这个效果,这里我将介绍两个工具:一个是binlog-rollback,一个是MyFlash。

我们先来用binlog-rollback工具试试,这个工具就是一个perl脚本,binlog-rollback.pl,开始进行恢复。(code5)

[ ~]# perl binlog-rollback.pl -f ‘/u01/mysql/3306/log/binlog/mysql-bin.000009‘ -h 127.0.0.1 -u ‘root‘ -p ‘xxx‘ -P 3306 -i -o ‘/tmp/t.sql‘

[ ~]# more /tmp/t.sql 
DELETE FROM `test`.`t1` WHERE `id`=2 AND `name`=‘ljp2‘;
DELETE FROM `test`.`t1` WHERE `id`=1 AND `name`=‘ljp1‘;

可以看到,我们之前是insert两行数据,这里反向解析成了delete,达到了我们的目的。同样也可以想象 当你误删除了数据,反解SQL就会是insertSQL了,这样就达到了恢复数据的目的。但是这个工具我们也看到,是需要输入用户名密码的。(code5)

接下来我们再来试试MyFlash,这个工具是由美团大佬开源的。我们同样来进行反解SQL试试。(code6)

[ MyFlash-master]# ./binary/flashback --binlogFileNames=/u01/mysql/3306/log/binlog/mysql-bin.000009

[ MyFlash-master]# mysqlbinlog --base64-output=decode-rows -v binlog_output_base.flashback
......
### DELETE FROM `test`.`t1`
### WHERE
###   @1=1
###   @2=‘ljp1‘
### DELETE FROM `test`.`t1`
### WHERE
###   @1=2
###   @2=‘ljp2‘
......

可以看到,这个工具与前面讲到的工具不太一样,首先反解binlog的时候,不需要输入用户名密码,并且生成binlog_output_base.flashback文件,这个文件是反解后的二进制文件。生成这种文件进行恢复的好处就是,要比binlog-rollback工具生成SQL语句方式 快很多。同时,我们也进行了查看,确实与我们之前执行的正好相反,由insert两行数据,生成最后的delete两行数据,从而达到数据恢复的目的。(code6)

再深入一步,为了帮助我们可以更快地恢复数据,这里我来延伸讲一下,可以看到,通过binlog恢复,其实还是比较麻烦的。接下来引入一个新的概念:延迟从库,来看下简单的架构是什么样子的。(图4)
mysql binlog数据恢复
其中红色部分,就是需要我们单独增加的从库,对它进行延迟处理,比如延迟24小时。当有人误操作了,DBA会在24小时内接到通知,由于延迟从库还是24小时之前的数据,登陆这个延迟从库,直接查询就可以了。(图4)

今天的分享到这里就结束了,最后我来给你总结一下。开头部分,我先是讲了数据快速恢复的必要性,核心就在于:如何在最短的时间内 进行数据恢复,避免企业损失。

但是如何做能够快速恢复,这与你的备份方式相关。就这个点来说,我们讲了一般的备份策略,包括全量备份、增量备份。由于今天主要是讲解binlog的恢复方式,因此我进一步讲了binlog的三种模式:Statement Level、Row Level、Maxed模式。一般线上数据库系统,采用Row Level模式。

这种模式下,我们拿MySQL 5.7.21做了举例讲解,插入两条数据,进行了正向、反向恢复。正向直接利用MySQL自带工具mysqlbinlog,反向利用binlog-rollback、美团的MyFlash。这两种工具是有本质区别的,binlog-rollback直接生成SQL,对于我们更加友好,但当数据量较大时,美团MyFlash是比较快的。当然今天只介绍了常用的两种工具,业内工具还是很多的,大家可以去GitHub上搜索。

最后,我延伸讲了一下,为了更快地恢复数据,我们可以引入延迟从库,对从库进行延迟处理,这也能够从另一个角度 进行数据的快速恢复。(图5)
mysql binlog数据恢复
好,希望我的分享可以帮助到你,也希望你在下方的留言区和我参与讨论。

相关推荐