TIP
本文主要是介绍 主从复制-binlog介绍和作用 。
# mysql binlog介绍以及通过binlog实现数据恢复
mysql的二进制日志binlog可以说是mysql最重要的日志,它记录了所有数据更新sql,以事件形式记录,还包含语句所执行的消耗的时间,mysql的二进制日志是事务安全型的。binlog日志主要用于mysql主从复制和数据恢复。
原文地址:代码汇个人博客 http://www.codehui.net/info/66.html
# 简单了解binlog
binlog是一个二进制格式的文件,用于记录用户对数据库更新的sql语句信息,但是不包括select和show这类操作,因为这类操作对数据本身并没有修改。然后,若操作本身并没有导致数据库发生变化,那么该操作也会写入二进制日志。
默认情况下,binlog日志是二进制格式的,不能使用查看文本工具的命令(比如,cat,vi等)查看,而使用mysqlbinlog解析查看。
一般来说开启二进制日志大概会有1%的性能损耗,其他介绍可以查看 官网文档介绍 (opens new window)
# binlog作用
# binlog日志有两个最重要的使用场景
1、mysql主从复制
MySQL Replication在Master端开启binlog,Master把它的二进制日志传递给slaves来达到master-slave数据一致的目的。
2、数据恢复
通过使用mysqlbinlog程序处理二进制日志文件,来使恢复数据。
# binlog日志包括两类文件
- 1、二进制日志索引文件(文件名后缀为.index)用于记录所有的二进制文件
- 2、二进制日志文件(文件名后缀为.00000*)记录数据库所有的DDL和DML(除了数据查询语句select)语句事件。在mysql 主从复制 (opens new window)中有见到。
# 开启binlog日志
1、修改配置文件
可以通过如下命令查看mysql读取的配置文件,顺序排前的优先
root@ba586179fe4b:/# mysql --help|grep 'my.cnf'
order of preference, my.cnf, $MYSQL_TCP_PORT,
/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf ~/.my.cnf
编辑配置文件,然后重启mysql
root@ba586179fe4b:/# vi /etc/my.cnf
[mysqld]
# 开启二进制日志功能,mysql-bin 是日志的基本名或前缀名
log-bin=mysql-bin
2、登录数据库 mysql -u root -p123456
查看binlog日志是否开启,log_bin为ON表示开启binlog日志
mysql> show variables like 'log_%';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| log_bin | ON |
| log_bin_trust_function_creators | OFF |
| log_error | |
| log_output | FILE |
| log_queries_not_using_indexes | OFF |
| log_slave_updates | OFF |
| log_slow_queries | OFF |
| log_warnings | 1 |
+---------------------------------+-------+
8 rows in set (0.00 sec)
# binlog日志常用操作命令
1、查看所有binlog日志列表
mysql> show master logs;
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| mysql-bin.000001 | 8184 |
| mysql-bin.000002 | 107 |
+------------------+-----------+
2 rows in set (0.00 sec)
2、查看master状态,即最后(最新)一个binlog日志的编号名称,及其最后一个操作事件pos结束点(Position)值
mysql> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000002 | 107 | | |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
3、刷新log日志,自此刻开始产生一个新编号的binlog日志文件
mysql> flush logs;
Query OK, 0 rows affected (0.01 sec)
4、重置(清空)所有binlog日志
mysql> reset master;
Query OK, 0 rows affected (0.01 sec)
# 查看binlog日志内容,常用有两种方式
# 1、使用mysqlbinlog自带查看命令
注: binlog是二进制文件,普通文件查看器cat more vi等都无法打开,必须使用自带的 mysqlbinlog 命令查看,binlog日志与数据库文件在同目录中(我的环境配置安装是选择在/usr/local/mysql/data中) 在MySQL5.5以下版本使用mysqlbinlog命令时如果报错,就加上"--no-defaults"选项
mysql数据存放在/var/lib/mysql
目录,通过mysqlbinlog打开日志文件
/usr/local/mysql/bin/mysqlbinlog /var/lib/mysql/mysql-bin.000001
截取最后一次执行的日志片段
# at 1778
#190221 6:58:51 server id 1 end_log_pos 1899 Query thread_id=1 exec_time=0 error_code=0
SET TIMESTAMP=1550732331/*!*/;
INSERT INTO `proxy` (`id`, `name`) VALUES ('6', 'code')
/*!*/;
# at 1899
#190221 6:58:51 server id 1 end_log_pos 1926 Xid = 45
- 主要参数解释:
- server id 1 : 数据库主机的服务号;
- end_log_pos 1899: sql结束时的pos节点
- thread_id=1: 线程号
# 2、使用show binlog events命令查看
上面这种办法读取出binlog日志的全文内容较多,不容易分辨查看pos点信息,这里介绍一种更为方便的查询命令:
mysql> 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 查询总条数(不指定就是所有行)
mysql> show master logs;
+------------------+-----------+
| Log_name | File_size |
+------------------+-----------+
| mysql-bin.000001 | 326 |
+------------------+-----------+
1 row in set (0.00 sec)
mysql> show binlog events in 'mysql-bin.000001'\G;
*************************** 1. row ***************************
Log_name: mysql-bin.000001
Pos: 4
Event_type: Format_desc
Server_id: 1
End_log_pos: 107
Info: Server ver: 5.5.62-log, Binlog ver: 4
*************************** 2. row ***************************
Log_name: mysql-bin.000001
Pos: 107
Event_type: Query
Server_id: 1
End_log_pos: 178
Info: BEGIN
*************************** 3. row ***************************
Log_name: mysql-bin.000001
Pos: 178
Event_type: Query
Server_id: 1
End_log_pos: 299
Info: use `codehui`; INSERT INTO `proxy` (`id`, `name`) VALUES ('6', 'code')
*************************** 4. row ***************************
Log_name: mysql-bin.000001
Pos: 299
Event_type: Xid
Server_id: 1
End_log_pos: 326
Info: COMMIT /* xid=61 */
4 rows in set (0.00 sec)
ERROR:
No query specified
mysql>
上面这条语句可以将指定的binlog日志文件,分成有效事件行的方式返回,并可使用limit指定pos点的起始偏移,查询条数。
- 查询第一个(最早)的binlog日志:
mysql> show binlog events\G;
- 指定查询 mysql-bin.000021 这个文件:
mysql> show binlog events in 'mysql-bin.000021'\G;
- 指定查询 mysql-bin.000021 这个文件,从pos点:8224开始查起:
mysql> show binlog events in 'mysql-bin.000021' from 8224\G;
- 指定查询 mysql-bin.000021 这个文件,从pos点:8224开始查起,查询10条
mysql> show binlog events in 'mysql-bin.000021' from 8224 limit 10\G;
- 指定查询 mysql-bin.000021 这个文件,从pos点:8224开始查起,偏移2行,查询10条
mysql> show binlog events in 'mysql-bin.000021' from 8224 limit 2,10\G;
# 通过binlog数据恢复
这个数据测试比较麻烦,我们先模拟个场景
codehui数据库会在每天凌晨1点使用计划任务进行一次备份,这里先手动执行一下备份任务。然后就有了数据库截止今天凌晨1点的数据库备份文件。早上9点和中午12点数据库都执行了增删改操作,然后下午18点直接删掉了codehui数据库,场景大概就是这样,下面进行测试数据的恢复。
先在codehui数据库插入测试数据
mysql> use codehui;
Database changed
mysql> CREATE TABLE `test` (
-> `id` int(11) NOT NULL AUTO_INCREMENT,\
-> `name` varchar(255) CHARACTER SET utf8 DEFAULT NULL,
-> PRIMARY KEY (`id`)
-> ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
Query OK, 0 rows affected (0.10 sec)
mysql> desc test;
+-------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
+-------+--------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
mysql> INSERT INTO `test` (`id`, `name`) VALUES ('1', 'code');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO `test` (`id`, `name`) VALUES ('2', 'php');
Query OK, 1 row affected (0.04 sec)
mysql> select * from test;
+----+------+
| id | name |
+----+------+
| 1 | code |
| 2 | php |
+----+------+
2 rows in set (0.00 sec)
mysql>
1、先备份一下数据库
备份数据库方法mysqldump,详细请参见 mysql 数据备份 (opens new window)
root@ba586179fe4b:/# /usr/local/mysql/bin/mysqldump -uroot -p123456 -B -F -R -x --master-data=2 codehui|gzip > /opt/backup/codehui.bak.sql.gz
root@ba586179fe4b:/# ls /opt/backup
codehui.bak.sql.gz
mysqldump备份方法参数说明:
-B:指定数据库 -F:刷新日志 -R:备份存储过程等 -x:锁表
--master-data:在备份语句里添加CHANGE MASTER语句以及binlog文件及位置点信息
由于上面在全备份的时候使用了-F选项,那么当数据备份操作刚开始的时候系统就会自动刷新log,这样就会自动产生一个新的binlog日志,这个新的binlog日志就会用来记录备份之后的数据库"增删改"操作
mysql> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000003 | 107 | | |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
也就是说, mysql-bin.000003 是用来记录凌晨1点之后对数据库的所有"增删改"操作。
2、早上9点对数据库进行"增"操作,新插入3条数据
mysql> INSERT INTO `test` (`id`, `name`) VALUES ('3', 'java'),('4','golang'),('5','shell');
Query OK, 3 rows affected (0.02 sec)
Records: 3 Duplicates: 0 Warnings: 0
mysql> select * from test;
+----+--------+
| id | name |
+----+--------+
| 1 | code |
| 2 | php |
| 3 | java |
| 4 | golang |
| 5 | shell |
+----+--------+
5 rows in set (0.00 sec)
3、中午12点对数据库进行"改"操作,修改1条数据
mysql> UPDATE `test` SET `name`='mysql' WHERE `id`='1';
Query OK, 1 row affected (0.02 sec)
Rows matched: 1 Changed: 1 Warnings: 0
mysql> select * from test;
+----+--------+
| id | name |
+----+--------+
| 1 | mysql |
| 2 | php |
| 3 | java |
| 4 | golang |
| 5 | shell |
+----+--------+
5 rows in set (0.00 sec)
4、下午18点,某程序员因心情不爽准备跑路,删掉了数据库codehui
mysql> drop database codehui;
Query OK, 3 rows affected (0.14 sec)
5、此刻先别慌,他忘记了我们还有大招,就是binlog日志。 先仔细查看最后一个binlog日志,并记录下关键的pos点,到底是哪个pos点的操作导致了数据库的破坏(通常在最后几步);
我们先备份一下最后一个binlog日志
root@ba586179fe4b:/# cp -v /var/lib/mysql/mysql-bin.000003 /opt/backup/
'/var/lib/mysql/mysql-bin.000003' -> '/opt/backup/mysql-bin.000003'
此时执行一次刷新日志索引操作,重新开始新的binlog日志记录文件。理论说 mysql-bin.000003 这个文件不会再有后续写入了(便于我们分析原因及查找pos点),以后所有数据库操作都会写入到下一个日志文件;
mysql> flush logs;
Query OK, 0 rows affected (0.03 sec)
mysql> show master status;
+------------------+----------+--------------+------------------+
| File | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+------------------+----------+--------------+------------------+
| mysql-bin.000004 | 107 | | |
+------------------+----------+--------------+------------------+
1 row in set (0.00 sec)
6、读取日志 分析问题,读取日志方法上面已经说到,这里使用第二种
mysql> show binlog events in 'mysql-bin.000003';
+------------------+------+-------------+-----------+-------------+----------------------------------------------------------------------------------------------------+
| Log_name | Pos | Event_type | Server_id | End_log_pos | Info |
+------------------+------+-------------+-----------+-------------+----------------------------------------------------------------------------------------------------+
| mysql-bin.000003 | 4 | Format_desc | 1 | 107 | Server ver: 5.5.62-log, Binlog ver: 4 |
| mysql-bin.000003 | 107 | Query | 1 | 178 | BEGIN |
| mysql-bin.000003 | 178 | Query | 1 | 327 | use `codehui`; INSERT INTO `test` (`id`, `name`) VALUES ('3', 'java'),('4','golang'),('5','shell') |
| mysql-bin.000003 | 327 | Xid | 1 | 354 | COMMIT /* xid=184 */ |
| mysql-bin.000003 | 354 | Query | 1 | 425 | BEGIN |
| mysql-bin.000003 | 425 | Query | 1 | 544 | use `codehui`; INSERT INTO `proxy` (`id`, `name`) VALUES ('1', 'my') |
| mysql-bin.000003 | 544 | Xid | 1 | 571 | COMMIT /* xid=188 */ |
| mysql-bin.000003 | 571 | Query | 1 | 642 | BEGIN |
| mysql-bin.000003 | 642 | Query | 1 | 784 | use `codehui`; UPDATE `proxy` SET `name`='mysql' WHERE (`id`='1') AND (`name`='my') LIMIT 1 |
| mysql-bin.000003 | 784 | Xid | 1 | 811 | COMMIT /* xid=190 */ |
| mysql-bin.000003 | 811 | Query | 1 | 882 | BEGIN |
| mysql-bin.000003 | 882 | Query | 1 | 995 | use `codehui`; UPDATE `test` SET `name`='mysql' WHERE `id`='1' |
| mysql-bin.000003 | 995 | Xid | 1 | 1022 | COMMIT /* xid=192 */ |
| mysql-bin.000003 | 1022 | Query | 1 | 1109 | drop database codehui |
| mysql-bin.000003 | 1109 | Rotate | 1 | 1152 | mysql-bin.000004;pos=4 |
+------------------+------+-------------+-----------+-------------+----------------------------------------------------------------------------------------------------+
15 rows in set (0.00 sec)
到这里是不很兴奋,看到了备份之后执行的所有的"增删改"记录。 通过分析,造成数据库破坏的pos点区间是介于 1022--1109 之间(这是按照日志区间的pos节点算的),只要恢复到1109前就可。
7、恢复凌晨1点的备份数据,也就是刚才手动备份的数据
root@ba586179fe4b:/# cd /opt/backup/
root@ba586179fe4b:/opt/backup# ls
codehui.bak.sql.gz mysql-bin.000003
root@ba586179fe4b:/opt/backup# gzip -d codehui.bak.sql.gz
root@ba586179fe4b:/opt/backup# mysql -uroot -p123456 -v < codehui.bak.sql
这样就恢复了截至当日凌晨(1:00)前的备份数据都恢复了,之后的数据通过binlog日志mysql-bin.000003进行恢复。
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| codehui |
| mysql |
| performance_schema |
+--------------------+
4 rows in set (0.00 sec)
mysql> use codehui;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> show tables;
+-------------------+
| Tables_in_codehui |
+-------------------+
| demo |
| proxy |
| test |
+-------------------+
3 rows in set (0.00 sec)
mysql> select * from test;
+----+------+
| id | name |
+----+------+
| 1 | code |
| 2 | php |
+----+------+
2 rows in set (0.00 sec)
8、从binlog日志恢复数据
恢复命令的语法格式:
mysqlbinlog mysql-bin.0000xx | mysql -u用户名 -p密码 数据库名
- 常用参数选项解释:
- --start-position=875 起始pos点
- --stop-position=954 结束pos点
- --start-datetime="2016-9-25 22:01:08" 起始时间点
- --stop-datetime="2019-9-25 22:09:46" 结束时间点
- --database=codehui 指定只恢复codehui数据库(一台主机上往往有多个数据库,只限本地log日志)
- 不常用选项:
- -u --user=name 连接到远程主机的用户名
- -p --password[=name] 连接到远程主机的密码
- -h --host=name 从远程主机上获取binlog日志
- --read-from-remote-server 从某个MySQL服务器上读取binlog日志
小结:实际是将读出的binlog日志内容,通过管道符传递给mysql命令。这些命令、文件尽量写成绝对路径;
A、 完全恢复(需要手动编辑mysql-bin.000003,将那条drop语句剔除掉)
温馨提示:在恢复全备数据之前必须将该binlog文件移出,否则恢复过程中,会继续写入语句到binlog,最终导致增量恢复数据部分变得比较混乱!
root@ba586179fe4b:/opt/backup# mysqlbinlog /opt/backup/mysql-bin.000003 > /opt/backup/000003.sql
root@ba586179fe4b:/opt/backup# vi /opt/backup/000003.sql #删除里面的drop语句
# 删掉drop语句前后的# at 到 /*!*/之间的内容
root@ba586179fe4b:/opt/backup# mysql -uroot -p123456 -v < /opt/backup/000003.sql
查看数据,已经恢复了
mysql> use codehui;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select * from test;
+----+--------+
| id | name |
+----+--------+
| 1 | mysql |
| 2 | php |
| 3 | java |
| 4 | golang |
| 5 | shell |
+----+--------+
5 rows in set (0.00 sec)
# 然后删除codehui数据库,测试第二种方法
mysql> drop database codehui;
Query OK, 3 rows affected (0.06 sec)
# 重新导入凌晨1点的备份数据,
root@ba586179fe4b:/opt/backup# mysql -uroot -p123456 -v < codehui.bak.sql
B、 指定pos结束点恢复(部分恢复):
--stop-position=571 pos结束节点(按照事务区间算,是571)
- 注意:
- 此pos结束节点介于"test"表原始数据与更新"name='mysql'"之前的数据,这样就可以恢复到 更改"name='mysql'"之前的数据了。
操作如下
root@ba586179fe4b:/opt/backup# mysqlbinlog --stop-position=571 --database=codehui /var/lib/mysql/mysql-bin.000003 | mysql -uroot -p123456 -v codehui
mysql> use codehui;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select * from test;
+----+--------+
| id | name |
+----+--------+
| 1 | code |
| 2 | php |
| 3 | java |
| 4 | golang |
| 5 | shell |
+----+--------+
5 rows in set (0.00 sec)
C、 指定pos点区间恢复(部分恢复):
更新 "name='mysql'" 这条数据,日志区间是Pos[882] --> End_log_pos[995],按事务区间是:Pos[811] --> End_log_pos[1022]
单独恢复 "name='mysql'" 这步操作,可这样: 按照binlog日志区间单独恢复:
root@ba586179fe4b:/opt/backup# mysqlbinlog --start-position=882 --stop-position=995 --database=codehui /var/lib/mysql/mysql-bin.000003 | mysql -uroot -p123456 -v codehui
按照事务区间单独恢复
root@ba586179fe4b:/opt/backup# mysqlbinlog --start-position=811 --stop-position=1022 --database=codehui /var/lib/mysql/mysql-bin.000003 | mysql -uroot -p123456 -v codehui
如果要恢复区间内的多条日志,按事务区间恢复就可以。
mysql> use codehui;
Reading table information for completion of table and column names
You can turn off this feature to get a quicker startup with -A
Database changed
mysql> select * from test;
+----+--------+
| id | name |
+----+--------+
| 1 | mysql |
| 2 | php |
| 3 | java |
| 4 | golang |
| 5 | shell |
+----+--------+
5 rows in set (0.00 sec)
查看数据库恢复了"name='mysql'",这样就恢复了删除前的数据状态了。
D、 也可指定时间区间恢复(部分恢复):除了用pos点的办法进行恢复,也可以通过指定时间区间进行恢复,按时间恢复需要用mysqlbinlog命令读取binlog日志内容,找时间节点。
# 起始时间点
--start-datetime="YYYY-MM-DD H:I:S"
# 结束时间点
--stop-datetime ="YYYY-MM-DD H:I:S"
# 用法举例
mysqlbinlog --start-position=811 --start-datetime="YYYY-MM-DD H:I:S" --stop-datetime="YYYY-MM-DD H:I:S" --database=codehui /var/lib/mysql/mysql-bin.000003 | mysql -uroot -p123456 -v codehui
# 参考文章
- https://www.jianshu.com/p/e1cacf8cceff