mysql中的binlog日志介绍及恢复数据

本文中所使用的MySQL版本:5.6.44

本文中所使用的测试数据表都是基于:INNODB引擎

mysql中的binlog日志是什么?

MySQL的binlog就是大家经常所说的Binary Log,也就是我们常说的binlog,即 二进制日志,二进制日志记录了MySQL所有修改数据库的操作,然后以二进制的形式记录日志在日志文件中,其中还包括没每条语句所执行的时间和消耗的资源,以及相关的事务信息。


binlog记录了对MySQL数据库执行更改的所有操作,但是不包括SELECT和SHOW这类操作,因为这类操作对数据本身并没有修改。然后,若操作本身并没有导致数据库发生变化,那么该操作也会写入二进制日志。默认情况下二进制日志功能是没有开启的,开启二进制日志功能可以在mysql中的配置文件里配置 log-bin[=file_name] 来开启二进制日志。


binlog日志的作用:

用于MySQL主从复制

增量数据备份(不是所有数据备份,而是最近的写操作) 及 恢复误操作的数据


binlog日志在INNODB和MYISAM中的区别:

对于INNODB存储引擎的表:commit后才会写binlog,如果语句被cancel,作为事务性引擎,会回滚,不会出现更新一半,也不会记binlog。


对于MYISAM存储引擎的表:只要语法和语义没有错误,MYSQL不等执行语句执行完,就会写binlog。如果binlog_format=statement对于复制而言就很危险,因为主库可能会将它执行到一半的时候cancel(取消),然后这条语句却会在从库上完整的执行,造成主从数据不一致。


可见使用innodb表相比myisam无事务的表来说,从很多方面来看都安全很多。另外binlog可以恢复所有存储引擎的数据。

binlog日志的启动和关闭

查看是否开启binlog日志:SHOW VARIABLES LIKE '%log_bin%';

示例截图:

m1.png

如果log_bin的值是OFF 表示binlog日志是关闭状态


如何开启binlog日志?

找到mysql的配置文件,windows上一般是my.ini,而在linux上一般是my.cnf。无所谓,只要你自己知道你的mysql配置文件使用的是那个就好。


在mysql配置文件中加入如下2个配置选项:

server-id=1

log-bin=mysql-bin #开启binlog日志


然后重启mysql服务后,再次使用SHOW VARIABLES LIKE '%log_bin%'; 命令查看binlog日志是否开启成功。

示例截图:

m2.png

m3.png

log_bin的值是 ON 表示binlog日志成功开启

log_bin_basename:表示二进制的文件保存路径


注意:一般网上的大部分开启binlog日志的答案都是mysql配置文件中加入:log-bin=mysql-bin 这一行配置项就行了,但是只使用这个可能会导致一些错误或不稳定性,所以推荐加入 server-id=1 这一行配置项。关于 server-id=1 代表什么意思,可以从本博客中的搜索框里搜索:mysql主从 然后点进去自行查看,或者自己上网查。


关于binlog日志的默认开启和关闭:额 一些集成环境貌似都是将binlog日志开启了,比如windows上的phpstudy、Visual NMP等。linux上的lnmp、宝塔等集成环境貌似也都是默认开启的~~~


查看当前二进制日志文件状态:  show master status;

show master status; 可以查看master数据库当前正在使用的二进制日志及当前执行二进制日志位置。

示例截图:

m1.png

对于binlog来说上图中的mysql-bin.xxx就是binlog的节点日志文件,而mysql-bin.index这个文件记录的是所有日志文件的地址


我这里使用的是lnmp环境,默认二进制日志文件路径为:/usr/local/mysql/var

示例截图如下:

m2.png

注意:上图中的mysql-bin.xxxxxx等文件可以删除,但是mysql-bin.index文件不能删除!


注意:如果重新启动了mysql服务,那么使用 show master status; 查看日志文件会发现mysql会重新生成了一份新的binlog日志文件(可以自己试一下,这里就不进行截图演示了),也就是说 一个binlog日志文件记录的是 从启动(或重启)之后的时间段的日志信息,直到mysql服务停止或者重新启动为止。


清空所有的binlog日志文件:reset master;

功能说明:删除所有的binglog日志文件,并将日志索引文件(mysql-bin.index 文件)中的内容清空,重新开始所有新的日志文件。


示例截图:

m1.png


再次进入/usr/local/mysql/var 目录 查看mysql的二进制日志文件

m2.png

上图中可以看到之前显示的mysql-bin.000014和mysql-bin.000015两个binlog日志文件已经被清空,binlog日志文件重新开始从mysql-bin.000001开始,然后查看 mysql-bin.index 文件,会发现里面记录的日志文件地址又是从 mysql-bin.000001 这个文件开始记录了


谨慎使用该命令。。不要随便使用这个命令。。


生成一个新的binlog日志文件:mysql> flush logs;  // 以后的binlog日志记录都会记录在这个新的日志文件中


查看当前binlog日志的格式:mysql> show  VARIABLES like '%binlog_format%' 

使用binlog日志进行数据恢复操作

前期准备工作:

先将mysql中的bin目录添加到环境变量中,为了方便我们下面操作不用写全路径。。

我这里安装的是lnmp的环境,lnmp环境中的mysql的bin目录路径为:/usr/local/mysql/bin

vi /etc/profile

#在文件中加入下面2行代码,mysql中的bin目录换成你自己的
PATH=$PATH:/usr/local/mysql/bin
export PATH

#然后保存退出

最后:执行命令 source /etc/profile 或 执行点命令 ./profile使其修改生效,执行完可通过echo $PATH 命令查看是否添加成功。


开始数据恢复操作演示:

添加一下测试的数据操作

CREATE DATABASE bin DEFAULT CHARACTER SET utf8;

use bin;

CREATE TABLE `t`  (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;

INSERT INTO t VALUES(null, 'php');
INSERT INTO t VALUES(null, 'mysql');


这个时候我们可以使用以下2中方式来查看我们的mysql中的二进制文件信息

由于我们上面执行了一遍reset master; 所以我们这里的binlog日志文件变成了 mysql-bin.000001 这个文件。


①、在终端中执行:mysqlbinlog ./mysql-bin.000001

注意:后面的 mysql-bin.000001 根据自己的实际情况来,别啥都不管一通复制。。最后发现结果不是你想要的或报错。。


示例截图:

m1.png

噗。。由于文件内容实在太多。。笔记本屏幕展示不全所有信息。。所以显示的完整日志文件内容如下:

/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=1*/;
/*!40019 SET @@session.max_insert_delayed_threads=0*/;
/*!50003 SET @OLD_COMPLETION_TYPE=@@COMPLETION_TYPE,COMPLETION_TYPE=0*/;
DELIMITER /*!*/;
# at 4
#190928 20:38:22 server id 1  end_log_pos 120 CRC32 0xd63e04c0 	Start: binlog v 4, server v 5.6.44-log created 190928 20:38:22 at startup
# Warning: this binlog is either in use or was not closed properly.
ROLLBACK/*!*/;
BINLOG '
PlSPXQ8BAAAAdAAAAHgAAAABAAQANS42LjQ0LWxvZwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAA+VI9dEzgNAAgAEgAEBAQEEgAAXAAEGggAAAAICAgCAAAACgoKGRkAAcAE
PtY=
'/*!*/;
# at 120
#190928 20:38:32 server id 1  end_log_pos 238 CRC32 0xf0d50a9f 	Query	thread_id=7	exec_time=0	error_code=0
SET TIMESTAMP=1569674312/*!*/;
SET @@session.pseudo_thread_id=7/*!*/;
SET @@session.foreign_key_checks=1, @@session.sql_auto_is_null=0, @@session.unique_checks=1, @@session.autocommit=1/*!*/;
SET @@session.sql_mode=1075838976/*!*/;
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=45/*!*/;
SET @@session.lc_time_names=0/*!*/;
SET @@session.collation_database=DEFAULT/*!*/;
CREATE DATABASE bin DEFAULT CHARACTER SET utf8
/*!*/;
# at 238
#190928 20:38:54 server id 1  end_log_pos 590 CRC32 0x749950bb 	Query	thread_id=7	exec_time=0	error_code=0
use `bin`/*!*/;
SET TIMESTAMP=1569674334/*!*/;
CREATE TABLE `t`  (
  `id` int(10) NOT NULL AUTO_INCREMENT,
  `name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact
/*!*/;
# at 590
#190928 20:39:08 server id 1  end_log_pos 667 CRC32 0x21928d55 	Query	thread_id=7	exec_time=0	error_code=0
SET TIMESTAMP=1569674348/*!*/;
BEGIN
/*!*/;
# at 667
# at 699
#190928 20:39:08 server id 1  end_log_pos 699 CRC32 0x0944f341 	Intvar
SET INSERT_ID=1/*!*/;
#190928 20:39:08 server id 1  end_log_pos 804 CRC32 0xe2cc779d 	Query	thread_id=7	exec_time=0	error_code=0
SET TIMESTAMP=1569674348/*!*/;
INSERT INTO t VALUES(null, 'php')
/*!*/;
# at 804
#190928 20:39:08 server id 1  end_log_pos 835 CRC32 0x747f6612 	Xid = 557
COMMIT/*!*/;
# at 835
#190928 20:39:10 server id 1  end_log_pos 912 CRC32 0x181bc814 	Query	thread_id=7	exec_time=0	error_code=0
SET TIMESTAMP=1569674350/*!*/;
BEGIN
/*!*/;
# at 912
# at 944
#190928 20:39:10 server id 1  end_log_pos 944 CRC32 0xc75b4910 	Intvar
SET INSERT_ID=2/*!*/;
#190928 20:39:10 server id 1  end_log_pos 1051 CRC32 0xaa340797 	Query	thread_id=7	exec_time=0	error_code=0
SET TIMESTAMP=1569674350/*!*/;
INSERT INTO t VALUES(null, 'mysql')
/*!*/;
# at 1051
#190928 20:39:10 server id 1  end_log_pos 1082 CRC32 0x1ea21d2e 	Xid = 558
COMMIT/*!*/;
DELIMITER ;
# End of log file
ROLLBACK /* added by mysqlbinlog */;
/*!50003 SET COMPLETION_TYPE=@OLD_COMPLETION_TYPE*/;
/*!50530 SET @@SESSION.PSEUDO_SLAVE_MODE=0*/;

通过以上二进制日志文件中的内容可以看到 上面我创建的数据库(bin)、数据表(t)、插入的2条数据 以及每一步操作的具体时间等,通通都有记录。不过这种方式不推荐使用。。因为看起来实在是不容易。。推荐下面这种第二种方式。。


②、使用show binlog events; 命令查看某个binlog二进制日志文件中的内容,也可以理解为 查看二进制日志中的事件

show binlog events [IN 'log_name'] [FROM pos] [LIMIT [offset,] row_count];

各个选项解析说明:

IN 'log_name' :指定要查询的binlog文件名(不指定就是第一个binlog文件)

FROM pos:指定从哪个pos起始点开始查起(不指定就是从整个文件首个pos点开始算)

LIMIT [offset,]:偏移量(不指定就是0)

row_count:查询总条数(不指定就是所有行)


这里的查看命令为:show binlog events in 'mysql-bin.000001';


示例截图如下:

m1.png

Log_name:顾名思义就是 查询的binlog日志的文件名

Pos:Pos的起始点

Event_type:事件类型

Server_id:标识是由哪台服务器执行的 

End_log_pos:Pos的结束点(即:下次的Pos的起始点)

Info:执行的sql语句信息


也可以 基于开始时间/结束时间 和 日期 查看binlog日志文件中的内容,示例如下:

mysqlbinlog --start-datetime='2021-03-01 00:00:00' --stop-datetime='2021-03-01 23:59:59' -d 数据库名 ./mysql-bin.000001(二进制文件)


③、接下来做一个操作就是将t表中的所有数据进行删除(这是模拟某些不小心的同学做的操作)

mysql> delete FROM t;
Query OK, 2 rows affected (0.00 sec)

mysql> select * from t;
Empty set (0.00 sec)


然后可以在mysql使用 show binlog events; 查看二进制日志文件信息。

注意:show binlog events; 这里没有指定'log_name',则显示的是 第一个binlog的二进制日志文件中的内容信息,即mysql-bin.000001


或者直接指定要显示哪一个的binlog日志文件信息:show binlog events in 'mysql-bin.000001';


示例截图如下:

m1.png


现在来做恢复;根据数据情况找到数据的节点位置, 在我们这里 发现节点是从590开始到1082结束


为了防止报错,还是进入mysql的binlog日志文件中执行吧,并且指定mysqlbinlog这个程序的全路径吧。

#进入保存binlog日志文件中的目录
/usr/local/mysql/var

#开始将起始节点为 590 到 结束节点为 1082 这段节点之间的数据恢复到mysql中
/usr/local/mysql/bin/mysqlbinlog mysql-bin.000001 --start-position 590 --stop-position 1082  | mysql -u root -p

然后按回车 然后输入密码即可


#/*********分割线************/

#除了以上这种直接恢复到数据库外,还可以将恢复的数据写入单个文件,然后将该文件导入到mysql中,这里只写一下相关命令,就不做截图演示了
shell> mysqlbinlog binlog.000002 >> /tmp/statements.sql  #将数据写入到一个文件 
shell> mysql -u root -p -e "source /tmp/statements.sql"  #将文件中的数据导入到mysql中

示例截图如下:

m2.png


m3.png

最后查看select * from t; 发现数据已经恢复了。


注意:我这里使用的是根据起始节点和结束节点的方式来恢复数据  除了这种方式外,也可以根据日期来恢复数据的。


删除跑路? 不存在的。  不慌 问题不大!不是只有DBA才能帮你找回数据   自己也可以!

binlog日志恢复弊端:

思考:如果是生产环境,binlog此种数据恢复手段会有怎样的弊端? 以下列举了大概3点弊端:


①、binlog日志记录的不单单是一个数据库的操作日志,可能对其它数据库也有记录相关的操作日志,那 如何从binlog日志中获取指定数据库的日志信息呢?


解决:mysqlbinlog工具加一个-d参数即可 -d即database的简写,使用示例如下:

-- 从mysql-bin.000001这个binlog日志文件中 截取起始节点590到结束节点1082之间的所有bin数据库操作的日志数据 并保存输出到/tmp/test.sql文件中  ps:相关参数 请根据自身的实际情况换成自己的,别啥都复制。。

/usr/local/mysql/bin/mysqlbinlog -d bin --start-position 590 --stop-position 1082 /usr/local/mysql/var/mysql-bin.000001 > /tmp/test.sql


②、需要的日志数据分布在多个binlog日志文件中,那这种跨日志文件的又该如何获(截)取想要的日志数据呢?


我们来举个栗子,看 以下场景:

    起点:假如说,我们需要的日志数据的起点在mysql-bin.000001这个binlog文件中,假设起始点是6600

    终点:一般都是binlog日志文件的最后一个文件,假设在mysql-bin.000010文件中,假设结束点是980


解决:像这种跨日志文件获取日志数据,就不能和上面一样直接使用起始点和结束点(起始点和结束点 也就是上面说的Pos和End_log_pos 以下就简称pos号了)进行恢复了。或者是只能使用一半的pos号,要么是前面使用pos号 后边就不能使用pos号、要么是后面使用pos号 前面就不能使用pos号。此时我们可以借助mysqlbinlog工具中的--start-datetime=时间 和 --stop-datetime=时间。这个两个参数,使用示例如下:

-- 1、从时间这个维度获取mysql-bin.000001 mysql-bin.000010两个binlog文件中符合条件的数据
/usr/local/mysql/bin/mysqlbinlog --start-datetime=时间 --stop-datetime=时间 /usr/local/mysql/var/mysql-bin.000001 mysql-bin.000010 > /tmp/test.sql

-- 2、时间和pos号混合搭配使用
/usr/local/mysql/bin/mysqlbinlog --start-position --stop-datetime=时间 /usr/local/mysql/var/mysql-bin.000001 mysql-bin.000010 > /tmp/test.sql

-- 注意:时间和pos号可以随意组合 可根据自身实际情况 自由搭配使用 活学活用,这里只列举了2条使用示例。。更多姿势。。等你发掘、、


③、假如说一个数据库创建了3年,期间一直在用数据库,binglog日志文件已经从mysql-bin.000001、mysql-bin.000002、mysql-bin.000003、mysql-bin.000004、mysql-bin.000005、mysql-bin.000006等等......记录了N多个binlog文件,突然间操作失误,该数据库被误删了。。这个时候又该如何使用binlog文件进行恢复?毕竟这么多binlog日志文件啊。。按照常理来说。。3年了。。数据量应该也挺多的了。。


说明:首先必须明白一点,这个恢复很难。。。第一 这个日志文件不一定能保存这么长时间!第二 即使日志能保存这么长时间 恢复起来要多痛苦有多难受!


解决:

    假设:每周六做一次全备份(晚上23:00),binlog每天备份(晚上23:00)。当然了,生产环境中的数据必须有备份。。

    故障点:周三 10点 核心数据库被误操作给drop掉了。


首先 拿 全备份的数据恢复到周六那个时间点。然后使用binlog日志把周六晚上23:00之后的 一直到drop操作之前的数据进行恢复,就可以了。这样,只需要恢复3-4天binlog即可。

binlog日志维护操作:

①、日志滚动

说明:所谓的日志滚动,意思就是说 生成一份新的binlog日志文件,让mysql将日志记录在新生成的binlog日志文件里面去。


手动触发机制:

    mysql> flush logs;  // 手动触发滚动日志(在mysql里面执行该命令,上面也有该命令的说明。)


    /usr/local/mysql/bin/mysqladmin -uroot -pxxx flush-logs  // 手动触发滚动日志,记得把mysqladmin工具的路径以及root账号的密码换成自己的。 


自动触发机制:

    mysql> select @@max_binlog_size;   // 查看binlog日志文件的大小限制,默认binlog日志文件的大小限制是1GB,当binlog日志文件的大小超过了1GB,则 自动滚动一次日志。(该命令在mysql里面执行)可以根据自己的需求 设置成合理的大小。


    /usr/local/mysql/bin/mysqldump -F  // 使用mysqldump工具做备份的时候,可以加上 -F 选项,也可以进行自动滚动日志。


    mysql数据库重新启动也会自动滚动日志

②、日志的删除

说明:默认情况下,binlog日志文件是不会被删除的。如何去删除日志呢?切记:不要使用rm命令去删除binlog日志,因为它不是数据库的一个行为,使用rm命令去删除binlog日志文件可能会造成意想不到的后果(比如说 造成日志损坏、日志不匹配、mysql主从出现异常 等......)。。 


自动删除机制:

    mysql> select @@expire_logs_days;  // 该命令查看的是mysql清除过期日志的时间,单位是:天。 expire_logs_days参数的值 默认是0,即 表示所有binlog日志永久都不会失效,不会自动删除。想要expire_logs_days参数永久生效的话,可以修改mysql的配置文件 在[mysqld]标签下增加 expire_logs_days=值    那expire_logs_days参数的值具体应该设置多少天比较合理呢?一般都是根据你的数据库全备份周期来设置的,假如说 你7天做一次全备份,那expire_logs_days=8 就可以这样设置,也就是一个全备份周期+1天,因为一个全备份可能要持续很长时间才能完成,所以最好在延长1天,也就是说,每8天清除一次binlog日志文件。一般在生产环境中expire_logs_days的值,建议最少设置为2个全备份周期+1天。接着上面说的那个例子 expire_logs_days=15


手动删除机制:

    mysql> purge binary logs to 'mysql-bin.000003';  // 删除mysql-bin.000001,mysql-bin.000002 这2个binlog日志文件。mysql-bin.000003这个binlog日志文件不会被删除。(该命名在mysql里去执行)


    mysql> purge binary logs before '2008-04-02 22:46:26';  // 按照时间去删除binlog日志文件


    mysql> reset master;  // 删除所有的binglog日志文件,并将日志索引文件(mysql-bin.index 文件)中的内容清空,重新开始所有新的日志文件。新的日志文件的起始值从000001开始(该命名在mysql里去执行)注意:该命令相当危险,如果在主从模式下,在主库上执行该命令,则主从必宕。该命令请谨慎使用!

简单总结:

①、使用binlog只能针对针对数据量不是很多的情况,真正的生产环境每个几个G的日志文件,不止是光靠binlog恢复的,还有更多其它的办法。(比如 每天都要将数据备份N遍等。。)


②、判断binlog日志的时间阶段与pos节点位置很重要,但是需要知道关于mysqlbinlog工具的很多参数。


③、binlog实际上是我们数据恢复时,配合备份一起恢复数据的手段。



不要输了就跑  不要疼了就抱怨  -->【华尔街】



声明:禁止任何非法用途使用,凡因违规使用而引起的任何法律纠纷,本站概不负责。

小周博客
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

精彩评论

全部回复 0人评论 7,777人参与

loading