这里有个建议,由于mysql默认是不开启binlog的,如果是线上数据库,或者比较重要的数据库,建议还是将binlog开启,而且格式设为ROW类型的。
防止出现问题时,也有恢复的退路,养成定期备份数据库的习惯。
通过命令,查看是否开启了binlog。
show variables like 'log_%';
数据恢复方法:
1.用最近的全量备份,然后在加上全量备份时间点后的binlog日志,然后先还原全量备份,在通过命令把binlog执行掉,这样数据就能还原了,这种方法,不限binlog的日志格式,ROW或者MIXED都行。但是随着业务量增大,可能单次还原数据库和执行binlog需要很长时间。
2.只用binlog日志,将binlog日志解析出来,生成反向原始sql,然后执行还原数据,这种方式binlog日志格式需要是ROW
下面开始模拟,数据修改出错,或者数据误删进行数据恢复,基于第二种方法。
CREATE TABLE `table_test1` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT '' COMMENT '姓名',
`sex` int(1) DEFAULT '1' COMMENT '性别',
`age` int(3) DEFAULT '18' COMMENT '年龄',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
insert into binlog_test1.table_test1 VALUES(1,'张三1',1,19);
insert into binlog_test1.table_test1 VALUES(2,'李四1',1,19);
insert into binlog_test1.table_test1 VALUES(3,'王五1',1,19);
数据插入进去后,我们通过mysqlbinlog 命令看看binlog日志内容
mysqlbinlog --no-defaults E:\......\mysql-5.6.35-winx64\logs\mysql-bin.000001
可以看到我们新增的数据都在日志中,随便抓一个片段,来解释下日志内容。
/*!*/;
# at 483
#210222 10:56:11 server id 1 end_log_pos 563 CRC32 0xc52b14e5 Query thread_id=1 exec_time=0 error_code=0
SET TIMESTAMP=1613962571/*!*/;
BEGIN
/*!*/;
# at 563
#210222 10:56:11 server id 1 end_log_pos 630 CRC32 0xbf00ad20 Table_map: `binlog_test1`.`table_test1` mapped to number 70
# at 630
#210222 10:56:11 server id 1 end_log_pos 686 CRC32 0x4473d9af Write_rows: table id 70 flags: STMT_END_F
BINLOG '
Sx0zYBMBAAAAQwAAAHYCAAAAAEYAAAAAAAEADGJpbmxvZ190ZXN0MQALdGFibGVfdGVzdDEABAMP
AwMClgAOIK0Avw==
Sx0zYB4BAAAAOAAAAK4CAAAAAEYAAAAAAAEAAgAE//ABAAAAB+W8oOS4iTEBAAAAEwAAAK/Zc0Q=
'/*!*/;
# at 686
#210222 10:56:11 server id 1 end_log_pos 717 CRC32 0x115267fc Xid = 21
COMMIT/*!*/;
# at 717
#210222 10:56:11 server id 1 end_log_pos 797 CRC32 0xb521ac31 Query thread_id=1 exec_time=0 error_code=0
SET TIMESTAMP=1613962571/*!*/;
BEGIN
#at 483:事件的起点
#210222 10:56:11:事件记录的时间
server id 1:服务器标识,默认1
end_log_pos 563:事件结束的位置,第366字节
CRC320xc52b14e5:checksum主从复制校验值
Xid = 21:事务id
先将 table_test1 表中,id为3的数据更改下
update table_test1 set sex=0 where id=3;
delete from table_test1 where id=2;
在通过命令看下日志
可以看到更改的数据在日志中,且是有库和表的信息的,说明不管多少个库和表,binlog是互不影响的,都是具体到哪个库里的哪个表,
这样后面还原数据的时候就可以指定库和表,防止数据量太大。
还可以用
show binlog events in "mysql-bin.000001"
还原数据之前可以先用命令查询下当前binlog的position并刷新下日志,这样当前需要还原的日志里面就不会有新数据进去了,会重新生成一个日志文件,这个步骤看需要。主要就是防止数据过多影响恢复。
#查询position
show master status
#刷新日志,可以不刷新,看需求
flush logs
先将binlog日志导出,
mysqlbinlog --no-defaults -v --base64-output=decode-rows --start-position=1265 --stop-position=1675 --database=binlog_test1 E:\......\mysql-5.6.35-winx64\logs\mysql-bin.000001 > E:\......\mysql-5.6.35-winx64\logs\data.sql
参数说明:
--no-defaults :不要读任何选项文件,不加这个命令会报错
--start-position= :起始pos点,例如:--start-position=110
--stop-position= :结束pos点,例如:--stop-position=200
--start-datetime= :起始时间,例如:--start-datetime="2020-07-18 19:00:00"
--stop-datetime= :截至时间,例如:--stop-datetime="2020-07-18 21:00:00"
--database= :指定哪个库,例如:-database=binlog_test1
-v :生成带注释的sql语句(这是重点,反向还原需要看这个sql)
-v -v:生成列的的描述信息备注等
--base64-output=decode-rows :binlog部分是否显示出来的,decode-rows表示不显示binglog部分
文件导出后,我们再看看文件内容是什么
能看到当时执行sql的完整sql,包括这条数据修改之前的数据,然后我们就可以手动更改还原数据。当然如果数据是批量更改的,且整个binlog中有很多日志,那么就需要过滤条件找到需要还原的数据日志,然后再通过工具,将日志批量转换成反向sql执行。
这里推荐一个逆向工具mysqlbinlog_flashback,
源码地址:https://github.com/58daojia-dba/mysqlbinlog_flashback
使用python实现的,环境python2.6,我试的时候用的2.7也没问题,然后需要装包,特别是pymysql
下载完源码后需要改2个地方,这是目前知道有问题的地方。
第一个是row_event.py,把charset_to_encoding注释掉,发现读取的时候少了编码也没啥问题,如果不注释掉会报错提示找不到,也不知道为啥这个版本的pymysql没有charset_to_encoding
第二个可能是作者的一个bug
这里判断版本大小的时候,如果pymysql的版本是 0.5.1 这样的是没问题的。但是如果版本是 0.10.1这样的,这个判断就是不对的。因为不知道这种判断存在多少,所以最粗暴的方式就是直接去改下pymysql的版本。
通过跟踪代码找到版本修改的地方,改成1.10.1,这样判断就ok了。
然后可以通过 Pycharm来运行项目,这种就可以调试代码,假如出问题可以找下原因,或者直接通过cmd来运行。
python mysqlbinlog_back.py --help
可以看看有哪些命令,顺便看看想想是否能正常运行,具体的可以看README.md,有详细介绍。
然后就是最重要的逆向sql生成命令了,
--host="127.0.0.1" --port=3306 --username="root" --password="**********" --schema="数据库名称" --tables="表名称" -S "需要生成的binlog文件" -L "从哪个position开始"
--host="127.0.0.1" --port=3306 --username="root" --password="abcdefg" --schema="test" --tables="table_test" -S "mysql-bin.000001" -L "4"
这里有几个注意点,首先第一个正常情况下,如果要还原数据,我们是不敢直接去连真实数据库去操作的,那么肯定要把binlog拷到本地来,在本地还原好数据后在同步到真实环境。
首先把真实环境的binlog拷到本地的binlog目录,binlog可能是mysql-bin.009999这样的。本地可能只到mysql-bin.000005这样的。如果直接在命令里面去读mysql-bin.009999,它会报错的。
这里可以根据
SHOW BINARY LOGS;
查下本地数据库的binlog列表,然后将本地的binlog备份下,然后把mysql-bin.009999重命令下就可以了,比如将mysql-bin.000001备份下然后删除,在将mysql-bin.009999重命名为mysql-bin.000001,这是第一个细节问题。
第二个如果不是直连真实环境,那么本地新建的数据库名称以及表名需要跟真实环境的保持一致,因为binlog里面是记录这些的,扫描的时候是根据这些进行匹配的。比如真实环境是test库,table_test表,那么本地还原环境也要这样。有这个库和这个表,因为需要根据表信息去生成字段,命令中schema和tables就是本地环境的库和表
--schema="test" --tables="table_test"
执行完命令后会在mysqlbinlog_flashback-master\log地址下生成flashback_binlog_为前缀的sql文件,打开文件可以看到里面的逆向sql。
可以看到我一开始是新增3条数据,这里逆向生成3条删除数据,之前执行的命令
update table_test1 set sex=0 where id=3;
delete from table_test1 where id=2;
逆向生成一个update和insert语句