MySQL数据增量恢复实践

第1章 系统环境

1.1 操作系统版本

查看操作系统的版本信息如下:

[root@mysql01 ~]# cat /etc/redhat-release
CentOS release 6.5 (Final)
[root@mysql01 ~]# uname -r
2.6.32-431.el6.x86_64
[root@mysql01 ~]# uname -m
x86_64

1.2 MySQL数据库版本

查看数据库的版本信息如下:

mysql> select version();
+------------+
| version()  |
+------------+
| 5.5.32-log |
+------------+
1 row in set (0.00 sec)

1.3 主机环境

主机名

IP地址

mysql01

192.168.1.102

mysql02

192.168.1.103

1.4 数据模拟

1.4.1 mysql01

该数据库作为数据恢复的测试库,无任何数据。

1.4.2 mysql02

新建一个数据库,数据库名为test,添加如下三个测试表:student(学生表),course(课程表),sc(选课表)。

q  创建数据库:

mysql> create database test character set utf8 collate utf8_general_ci;     #<==创建test数据库并设定字符集为uft8;
Query OK, 1 row affected (0.00 sec)
 
mysql> show create database test;   #<==查看创建数据库的命令


+----------+---------------------------------------------------------------+
| Database | Create Database                                               |
+----------+---------------------------------------------------------------+
| test     | CREATE DATABASE `test` /*!40100 DEFAULT CHARACTER SET utf8 */ |
+----------+---------------------------------------------------------------+
1 row in set (0.00 sec)

q  创建表

mysql> use test;            #<==切换到test数据库

Database changed

 

(1)创建student表:

drop table student;
create table student(
Sno int(10) NOT NULL COMMENT '学号',
Sname varchar(16) NOT NULL COMMENT '姓名',
Ssex char(2) NOT NULL COMMENT '姓别',
Sage tinyint(2) NOT NULL default '0' COMMENT '学生年龄',
Sdept varchar(16) default NULL COMMENT '学生所在系别',
PRIMARY KEY (Sno),
key index_name(Sname)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

 

(2)创建course表:

drop table course;
create table course(
Cno int(10) NOT NULL COMMENT '课程号',
Cname varchar(64) NOT NULL COMMENT '课程名',
Ccredit tinyint(2) NOT NULL COMMENT '学分',
PRIMARY KEY (Cno)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;

 

(3)创建SC表

drop table SC;
create table SC(
SCid int(12) NOT NULL auto_increment COMMENT '主键',
Cno int(10) NOT NULL COMMENT '课程号',
Sno int(10) NOT NULL COMMENT '学号',
Grade tinyint(2) NOT NULL COMMENT '学生成绩',
PRIMARY KEY (SCid)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;

q  查看表结构

使用desc table的命令查看表结构:

mysql> desc student;
+-------+-------------+------+-----+---------+-------+
| Field | Type        | Null | Key | Default | Extra |
+-------+-------------+------+-----+---------+-------+
| Sno   | int(10)     | NO   | PRI | NULL    |       |
| Sname | varchar(16) | NO   | MUL | NULL    |       |
| Ssex  | char(2)     | NO   |     | NULL    |       |
| Sage  | tinyint(2)  | NO   |     | 0       |       |
| Sdept | varchar(16) | YES  |     | NULL    |       |
+-------+-------------+------+-----+---------+-------+
5 rows in set (0.00 sec)

 

mysql> desc course;
+---------+-------------+------+-----+---------+-------+
| Field   | Type        | Null | Key | Default | Extra |
+---------+-------------+------+-----+---------+-------+
| Cno     | int(10)     | NO   | PRI | NULL    |       |
| Cname   | varchar(64) | NO   |     | NULL    |       |
| Ccredit | tinyint(2)  | NO   |     | NULL    |       |
+---------+-------------+------+-----+---------+-------+
3 rows in set (0.01 sec)

 

mysql> desc SC;
+-------+------------+------+-----+---------+----------------+
| Field | Type       | Null | Key | Default | Extra          |
+-------+------------+------+-----+---------+----------------+
| SCid  | int(12)    | NO   | PRI | NULL    | auto_increment |
| Cno   | int(10)    | NO   |     | NULL    |                |
| Sno   | int(10)    | NO   |     | NULL    |                |
| Grade | tinyint(2) | NO   |     | NULL    |                |
+-------+------------+------+-----+---------+----------------+
4 rows in set (0.00 sec)

q  插入表数据

(1)student表

INSERT INTO student values(0001, '宏志', '男',30, '计算机网络');
INSERT INTO student values(0002, '王锁', '男',30, 'computer application');
INSERT INTO student values(0003, 'oldboy', '男',28, '物流管理');
INSERT INTO student values(0004, '脉动', '男',29, 'computer application');
INSERT INTO student values(0005, 'oldgirl', '女',26, '计算机科学与技术');
INSERT INTO student values(0006, '莹莹', '女',22, '计算机护士');

 

(2)course表

INSERT INTO course values(1001, 'linux中高级运维',3);
INSERT INTO course values(1002, 'linux高级架构师',5);
INSERT INTO course values(1003, 'linux高级DBA',4);
INSERT INTO course values(1004, 'Python运维开发',4);
INSERT INTO course values(1005, 'Java web开发',3);

 

(3)SC表

INSERT INTO SC(Sno,Cno,Grade) values(0001,1001,4);
INSERT INTO SC(Sno,Cno,Grade) values(0001,1002,3);
INSERT INTO SC(Sno,Cno,Grade) values(0001,1003,1);
INSERT INTO SC(Sno,Cno,Grade) values(0001,1004,4);
INSERT INTO SC(Sno,Cno,Grade) values(0002,1001,3);

q  查看表数据插入结果

mysql> select * from student;
+-----+---------+------+------+--------------------------+
| Sno | Sname   | Ssex | Sage | Sdept                    |
+-----+---------+------+------+--------------------------+
|   1 | 宏志    | 男   |   30 | 计算机网络               |
|   2 | 王锁    | 男   |   30 | computer applica         |
|   3 | oldboy  | 男   |   28 | 物流管理                 |
|   4 | 脉动    | 男   |   29 | computer applica         |
|   5 | oldgirl | 女   |   26 | 计算机科学与技术         |
|   6 | 莹莹    | 女   |   22 | 计算机护士               |
+-----+---------+------+------+--------------------------+
6 rows in set (0.00 sec)

 

mysql> select * from course;
+------+----------------------+---------+
| Cno  | Cname                | Ccredit |
+------+----------------------+---------+
| 1001 | linux中高级运维      |       3 |
| 1002 | linux高级架构师      |       5 |
| 1003 | linux高级DBA         |       4 |
| 1004 | Python运维开发       |       4 |
| 1005 | Java web开发         |       3 |
+------+----------------------+---------+
5 rows in set (0.00 sec)

 

mysql> select * from SC;
+------+------+-----+-------+
| SCid | Cno  | Sno | Grade |
+------+------+-----+-------+
|    1 | 1001 |   1 |     4 |
|    2 | 1002 |   1 |     3 |
|    3 | 1003 |   1 |     1 |
|    4 | 1004 |   1 |     4 |
|    5 | 1001 |   2 |     3 |
+------+------+-----+-------+
5 rows in set (0.00 sec)

模拟数据创建完毕!!

第2章 数据恢复条件

2.1 全量备份数据文件

要对数据进行恢复,首先是要有备份的数据。为了防患于未然,我们需要定期对数据库进行全量备份。一般而言,我们可以通过执行定时任务对数据进行备份。

2.1.1 备份工具mysqldump

mysqldumpmysql自带的一个很好用的数据库备份命令,关键参数如下:

参数

含义

-B

备份多个库(添加createuse语句)

-A

备份所有库

-d

只备份库表结构

-t

只备份数据,无库表结构(sql语句形式)

-T

分离库表和数据称不同的文件,数据是文本,非SQL语句

-F

刷新binlog日志文件,生成新文件,将来增量恢复从这个文件开始!!

--master-data=value

增加binlog日志文件名及对应的位置点。value=1,不注释;value=2注释;

-x

--lock-all-tables,锁表

-l

--lock-tables,只读锁表

-q

--quick

-R

routines,指定转存的例程

 

2.1.2 全库整库备份

对整个数据库服务器的所有数据库进行备份,备份命令如下:

mysqldump -uroot -p123456 -S /data/3306/mysql.sock -A -B -F -R -x --master-data=2|gzip > /backup/all_$(date +%F).sql.gz

注:|gzip表示对备份文件进行压缩,压缩率大概为1/3

2.1.3 分数据库备份(推荐)

推荐使用分库备份的方式来进行备份,可以编写一个简单的shell脚本来实现该功能,简单脚本示例如下:

#!/bin/bash
 
password="123456"
user="root"
port="3306"
bakpath=/backup
database=`mysql -u$user -p$password -S /data/$port/mysql.sock -e "show databases;"| egrep -v "Database|_schema|mysql"`
 
for n in $database
do
  mysqldump -u$user -p$password -S /data/$port/mysql.sock -B -F -R -x --master-data=2 $n | gzip > ${bakpath}/${n}_$(date +%F).sql.gz
done

2.1.4 定时执行备份脚本

可以设置定时任务每天凌晨1:00备份数据库,定时任务设置如下:

00 01 * * * /bin/sh /service/scripts/backup.sh &>/dev/null

2.2 增量备份数据文件

除了定时全备数据库外,还需要数据库增量数据。该功能是通过binlog日志文件实现的。

2.2.1 binlog日志文件介绍

binlog日志作用是用来记录mysql内部增删改等对mysql数据库有更新的内容的记录(对数据库的改动),对数据库查询的语句如showselect开头的语句,不会被binlog日志记录。用于数据库的增量恢复,以及主从复制。

q  binlog的三种模式

(1)    ROW Level

日志中会记录成每一行数据被修改的形式,然后在slave端再对相同的数据进行修改。

(2)    Statement Level

每一条会修改数据的sql都会记录到masterbin-log中。slave在复制的时候sql进程会解析成和原来master端执行过程相同的sql来再次执行。

(3)    Mixed

实际上就是前两种模式的结合。在Mixed模式下,MySQL会根据执行的每一条具体的sql语句来区分对待记录的日志形式,也就是在statementRow之间选择一种。

2.2.2 开启log-bin日志功能

mysql配置文件里面添加如下参数开启该功能:

[root@mysql02 backup]# grep "log-bin" /data/3306/my.cnf

log-bin = /data/3306/mysql-bin          #<==开启binlog功能

2.3 条件小结

增量恢复的条件:存在一份全备之后的时刻到出问题的所有增量的binlog文件备份!!

第3章 数据库的恢复

场景:在一次做完全量备份之后,数据库test被无情的删除,期间有数据的增量写入。

3.1 环境模拟

3.1.1 执行一次全被(分库备份)

[root@mysql02 backup]# sh /service/scripts/test.sh      #<==直接执行脚本,脚本如上所示
[root@mysql02 backup]# ll
total 4
-rw-r--r-- 1 root root 1442 Feb  6 11:17 test_2018-02-06.sql.gz     #<==备份的test库数据

3.1.2 写入新的数据到SC

对表SC重新写入3条数据,之前是5条。

mysql> use test;
 
INSERT INTO SC(Sno,Cno,Grade) values(0002,1002,2);
INSERT INTO SC(Sno,Cno,Grade) values(0002,1003,2);
INSERT INTO SC(Sno,Cno,Grade) values(0002,1004,8);
 
mysql> select * from SC;        #<==更新过后有8条数据了
+------+------+-----+-------+
| SCid | Cno  | Sno | Grade |
+------+------+-----+-------+
|    1 | 1001 |   1 |     4 |
|    2 | 1002 |   1 |     3 |
|    3 | 1003 |   1 |     1 |
|    4 | 1004 |   1 |     4 |
|    5 | 1001 |   2 |     3 |
|    6 | 1002 |   2 |     2 |
|    7 | 1003 |   2 |     2 |
|    8 | 1004 |   2 |     8 |
+------+------+-----+-------+
8 rows in set (0.00 sec)

3.1.3 删除数据库

写入数据之后,删除test数据库:

mysql> drop database test;
Query OK, 3 rows affected (0.00 sec)
 
mysql> show databases;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)

3.2 全量数据恢复

3.2.1 解压gz数据文件

由于打包时是将数据文件压缩了,因此需要将压缩后的文件解压才能使用,解压如下:

[root@mysql02 backup]# ll
total 4
-rw-r--r-- 1 root root 1442 Feb  6 11:17 test_2018-02-06.sql.gz     #<==这是一个gz文件
[root@mysql02 backup]# gzip -d test_2018-02-06.sql.gz       #<==解压命令,最好先做个备份
[root@mysql02 backup]# ll
total 8
-rw-r--r-- 1 root root 4283 Feb  6 11:17 test_2018-02-06.sql    #<==解压过后的文件

3.2.2 恢复数据

恢复数据有两种方式,任意选择其中一种即可(推荐直接使用mysql命令):

方法一、直接通过mysql命令导入sql文件

[root@mysql02 backup]# mysql -uroot -p123456 -S /data/3306/mysql.sock < /backup/test_2018-02-06.sql 
[root@mysql02 backup]# mysql -uroot -p123456 -S /data/3306/mysql.sock -e "show databases;"
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| test               |
+--------------------+

方法二、登陆MySQL数据库过后,使用source恢复文件

mysql -uroot -p123456 -S /data/3306/mysql.sock
mysql> source /backup/test_2018-02-06.sql       #<==使用source 备份文件直接恢复


这一步骤完成之后已经恢复到全备时间点的数据了!!!

[root@mysql02 backup]# mysql -uroot -p123456 -S /data/3306/mysql.sock -e "use test;select * from SC;"
+------+------+-----+-------+
| SCid | Cno  | Sno | Grade |
+------+------+-----+-------+
|    1 | 1001 |   1 |     4 |
|    2 | 1002 |   1 |     3 |
|    3 | 1003 |   1 |     1 |
|    4 | 1004 |   1 |     4 |
|    5 | 1001 |   2 |     3 |
+------+------+-----+-------+

3.3 增量数据恢复

3.3.1 找到binlog文件

从数据库全被到数据库恢复的这段时间内,已经生成了多个的binglog文件,那么如何找到具体的文件呢?最好的办法就是通过对应的时间点来查找!!通过备份命令生成备份文件的时候,会有一个时间点,如下:

[root@mysql02 backup]# ll
total 8
-rw-r--r-- 1 root root 4283 Feb  6 11:17 test_2018-02-06.sql

可以看出是在11:17分做的备份,那么我们就可以找从11:17开始往后的binlog文件了:

[root@mysql02 backup]# ll /data/3306/
total 632
drwxr-xr-x 5 mysql mysql   4096 Feb  6 11:28 data
-rw-r--r-- 1 mysql mysql   2043 Jan 29 13:52 my.cnf
-rwx------ 1 mysql mysql   1110 Jan 29 13:50 mysql
-rw-r----- 1 mysql root   15803 Feb  5 16:04 mysql_3306.err
-rw-rw---- 1 mysql mysql    537 Jan 31 10:35 mysql-bin.000001
-rw-rw---- 1 mysql mysql    126 Jan 31 10:38 mysql-bin.000002
-rw-rw---- 1 mysql mysql    402 Jan 31 10:46 mysql-bin.000003
-rw-rw---- 1 mysql mysql    403 Jan 31 10:50 mysql-bin.000004
-rw-rw---- 1 mysql mysql    126 Jan 31 10:52 mysql-bin.000005
-rw-rw---- 1 mysql mysql   2256 Feb  5 15:57 mysql-bin.000006
-rw-rw---- 1 mysql mysql    126 Feb  5 15:59 mysql-bin.000007
-rw-rw---- 1 mysql mysql    264 Feb  5 16:01 mysql-bin.000008
-rw-rw---- 1 mysql mysql    477 Feb  5 16:03 mysql-bin.000009
-rw-rw---- 1 mysql mysql   7496 Feb  5 16:53 mysql-bin.000010
-rw-rw---- 1 mysql mysql 536268 Feb  6 09:54 mysql-bin.000011
-rw-rw---- 1 mysql mysql    150 Feb  6 10:16 mysql-bin.000012
-rw-rw---- 1 mysql mysql    150 Feb  6 10:17 mysql-bin.000013
-rw-rw---- 1 mysql mysql    237 Feb  6 10:18 mysql-bin.000014
-rw-rw---- 1 mysql mysql    150 Feb  6 10:18 mysql-bin.000015
-rw-rw---- 1 mysql mysql    235 Feb  6 11:17 mysql-bin.000016
-rw-rw---- 1 mysql mysql   7468 Feb  6 11:28 mysql-bin.000017
-rw-rw---- 1 mysql mysql    476 Feb  6 11:17 mysql-bin.index
-rw-rw---- 1 mysql mysql      6 Feb  5 16:04 mysqld.pid
srwxrwxrwx 1 mysql mysql      0 Feb  5 16:04 mysql.sock

可以直观的看出11:17以后的有mysql-bin.000017一个文件!

注:实现备份时切割刷新binlog文件是通过-F 参数指定的,所以在备份的时候要注意使用-F参数。这里也可能有多个文件,具体按实际情况

3.3.2 合并binlog文件

binlog文件默认是二进制文件无法读取,所以我们需要先将这些binlog文件转换为一个可读的SQL文件,而这一步骤可以通过mysqlbinlog命令来实现!!具体实现过程如下:

[root@mysql02 backup]# cp /data/3306/mysql-bin.000017 . #<==先将binlog文件拷贝到其他地方,注意备份

使用mysqlbinlog命令合并

 [root@mysql02 backup]# mysqlbinlog -d test mysql-bin.000017 >> recover.sql

注:1、这里的-d test是指定数据库。MySQL能识别binlog文件的最小单位就是数据库了,无法通过参数来识别具体的表。

    2、合并时要注意顺序,需按照递增的顺序来合并!!

3.3.3 删除SQL文件的错误语句

合并成一个文件之后,将错误的SQL命令删除,否则你重新执行的时候会将错误的命令再次执行,会出现一系列的问题!

注:这里需要在每条insert语句后面加一个;

SET TIMESTAMP=1517887356/*!*/;
drop database test          #<==这条就是误操作的命令,将之删除再做恢复操作!!!!
/*!*/;
# at 893

3.3.4 恢复增量数据

使用合并的文件来恢复增量数据,注意需要制定数据库,因为binlog里面是没有use 数据库语句的。

[root@mysql02 backup]# mysql -uroot -p123456 -S /data/3306/mysql.sock test < recover.sql

3.3.5 查看数据恢复情况

到上一步为止,数据的恢复工作基本已经完毕,这时候查看一下数据是否恢复正常!!

[root@mysql02 backup]# mysql -uroot -p123456 -S /data/3306/mysql.sock -e "use test;select * from SC;"
+------+------+-----+-------+
| SCid | Cno  | Sno | Grade |
+------+------+-----+-------+
|    1 | 1001 |   1 |     4 |
|    2 | 1002 |   1 |     3 |
|    3 | 1003 |   1 |     1 |
|    4 | 1004 |   1 |     4 |
|    5 | 1001 |   2 |     3 |
|    6 | 1002 |   2 |     2 |
|    7 | 1003 |   2 |     2 |
|    8 | 1004 |   2 |     8 |
+------+------+-----+-------+
增量数据恢复完毕!!!


第4章 单表的恢复

单表的恢复和上面类似,只不过不能直接导入SQL文件,需要将之转化为单表的SQL语句在导入。

4.1 grep日志文件恢复

4.1.1 过滤出SQL文件中单表的操作语句

使用grep 表明的形式,将binlog转换的SQL文件里面对应表的语句都筛选出来,再导入数据库中!

[root@mysql02 backup]# grep SC recover.sql          #<==过滤单表操作语句
INSERT INTO SC(Sno,Cno,Grade) values(0002,1002,2)
INSERT INTO SC(Sno,Cno,Grade) values(0002,1003,2)
INSERT INTO SC(Sno,Cno,Grade) values(0002,1004,8)
grep SC recover.sql > SC.sql

 

4.1.2 使用单表文件恢复

mysql -uroot -p123456 -S /data/3306/mysql.sock test < SC.sql
[root@mysql02 backup]# mysql -uroot -p123456 -S /data/3306/mysql.sock -e "use test;select * from SC;"
+------+------+-----+-------+
| SCid | Cno  | Sno | Grade |
+------+------+-----+-------+
|    1 | 1001 |   1 |     4 |
|    2 | 1002 |   1 |     3 |
|    3 | 1003 |   1 |     1 |
|    4 | 1004 |   1 |     4 |
|    5 | 1001 |   2 |     3 |
|    6 | 1002 |   2 |     2 |
|    7 | 1003 |   2 |     2 |
|    8 | 1004 |   2 |     8 |
+------+------+-----+-------+

4.2 测试库重新导出

先将全被和增被数据都导入到测试库里面,步骤同全量恢复数据。再此不重复操作。

4.2.1 导出表SQL文件

恢复好全库数据后,使用mysqldump命令将单表的SQL语句导出

mysqldump -uroot -p123456 -S /data/3307/mysql.sock -F -x test SC > sc.sql

4.2.2 恢复单表

将单表的SC.sql文件转移到需恢复的数据库,进行数据恢复!

mysql -uroot -p123456 -S /data/3306/mysql.sock test < SC.sql
[root@mysql02 backup]# mysql -uroot -p123456 -S /data/3306/mysql.sock -e "use test;select * from SC;"
+------+------+-----+-------+
| SCid | Cno  | Sno | Grade |
+------+------+-----+-------+
|    1 | 1001 |   1 |     4 |
|    2 | 1002 |   1 |     3 |
|    3 | 1003 |   1 |     1 |
|    4 | 1004 |   1 |     4 |
|    5 | 1001 |   2 |     3 |
|    6 | 1002 |   2 |     2 |
|    7 | 1003 |   2 |     2 |
|    8 | 1004 |   2 |     8 |
+------+------+-----+-------+

数据库恢复测试完毕!!!