MySQL全量、增量备份与恢复 (理论 实践篇)

时间:2022-05-16 06:54:14

数据备份的中要性

  • 在生产环境中,数据的安全性是至关重要的,任何数据的丢失都可能产生严重的后果
  • 造成数据丢失的原因
    • 程序错误
    • 人为错误
    • 计算机失败
    • 磁盘失败
    • 灾难(如起火、地震)和偷窃

数据库备份的分类

从物理与逻辑的角度,备份可分为

  • 物理备份:对数据库操作系统的物理文件(如数据文件、日志文件等)的备份
    • 物理备份又可以分为脱机备份(冷备份)和联机备份(热备份)
    • 冷备份:是关闭数据库的时候进行的
    • 热备份:数据库处于运行状态,这种备份方法依赖于数据库的日志文件
  • 逻辑备份:对数据库逻辑组件(如表等数据库对象)的备份

从数据库的备份策略角度,备份可分为

  • 完全备份:每次对数据进行完整的备份
  • 差异备份:备份那些自从上次完全备份之后被修改过的文件
  • 增量备份:只有那些在上次完全备份或者增量备份后修改的文件才会被备份

MySQL完全备份

  • 完全备份是对整个数据库的备份、数据库结构和文件结构的备份
  • 完全备份保存的是备份完成时刻的数据库
  • 完全备份是增量备份的基础

完全备份的优点

  • 备份与恢复操作简单方便

完全备份的缺点

  • 数据存在大量的重复
  • 占用大量的备份空间
  • 备份与恢复时间长

mysqldump备份数据库

  • MySQL数据库的备份可以采用多种方式
    • 直接打包数据库文件夹,如/usr/local/mysql/data
    • 使用专用备份工具 mysqldump
  • mysqldump命令
    • MySQL自带的备份工具,相当方便对MySQL进行备份
    • 通过该命令工具可以将指定的库、表或全部的库导出为SQL0脚本,在需要恢复时可进行数据恢复
  • mysqldump命令对单个库进行完全备份
    • `mysqldump -u 用户名 -p [密码] [选项] [数据库名] > /备份路径/备份文件名
  • 单库备份例子
    • mysqldump -u root -p auth > /backup/auth.sql
  • mysqldump命令对多个库进行完全备份
    • mysqldump -u 用户名 -p [密码] [选项] --databases 库名 1 [库名2] ... > /备份路径/备份文件名
  • 多库备份例子
    • mysqldump -u root -p --databases autth mysql > /backup/databases-auth-mysql.sql
  • 对所有库进行完全备份
    • mysqldump -u 用户名 -p [密码] [选项] --all-databases > /备份路径/备份文件名
  • 所有库备份例子
    • mysqldump -u root -p --opt --all-databases > /backup/all-data.sql

mysqldump备份表

  • 在实际生产环境中,存在对某个特定表的维护操作,此时mysqldump同样发挥重大作用
  • 使用mysqldump备份表的操作
    • mysqldump -u 用户名 -p [密码] [选项] 数据库名 表命 > /备份路径/备份文件名
  • 备份表的例子
    • mysqldump -u root -p mysql user > /backup/mysql-user.sql

恢复数据库

  • 使用mysqldump命令导出的SQL备份脚本,在进行数据恢复时可以使用以下方法导入
    • source命令
    • mysql命令
  • 使用source恢复数据库的步骤
    • 登录到MySQL数据库
    • 执行source 备份sql脚本的路径
  • source恢复例子
    • MySQL[(none)]>source /backup/all-data.sql
  • 使用mysql命令恢复数据
    • mysql -u 用户名 -p [密码] < 库备份脚本的路径
  • mysql命令恢复例子
    • mysql -u root -p < /backup/all-data.sql

恢复表的操作

  • 恢复表时同样可以使用source或者mysq|命令进行
  • source恢复表的操作与恢复库的操作相同
  • 当备份文件中只包含表的备份,而不包括创建库的语句时,必须指定库名,且目标库必须存在
    • mysq| -u 用户名 -p [密码] < 表备份脚本的路径
    • mysql -u root -P mysq| < /backup/mysql-user.sql
  • 在生产环境中,可以使用shell脚本自动实现定期备份

MySQL备份思路

  • 定期实施备份,制定备份计划或者策略,并严格遵守
  • 除了进行完全备份,开启MySQL服务器的日志功能是很重要的
    • 完全备份加上日志,可以对MySQL进行最大化还原
  • 使用统一的和易理解的备份文件名称
    • 不要使用backup1、backup2等这样没有意义的名字
    • 不推荐使用库名或者表名加上时间的命名规则

MySQL增量备份

使用mysqldump进行完全备份的存在的问题

  • 备份数据中有重复数据
  • 备份时间与恢复时间长

增量备份就是备份自.上一次备份之后增加或变化的文件或者内容

增量备份的特点

  • 没有重复数据,备份量不大,时间短
  • 恢复麻烦:需要上次完全备份及完全备份之后所有的增量备份才能恢复,而且要对所有增量备份进行逐个反推恢复

MySQL没有提供直接的增量备份方法

可以通过MySQL提供的二进制日志(binary logs)间接实现增量备份

MySQL二进制日志对备份的意义

  • 二进制日志保存了所有更新或者可能更新数据库的操作
  • 二进制日志在启动MySQL服务器后开始记录,并在文件达到max_binlog_size所设置的大小或者接收到flush logs命令后重新创建新的日志文件
  • 只需定时执行flush logs方法重新创建新的日志,生成二进制文件序列,并及时把这些旧的日志保存到安全的地方就完成了一个时间段的增量备份

MySQL数据库增量恢复

  • 一般恢复

    • 添加数据→进行完全备份→录入新的数据→进行增量备份→模拟故障→恢复操作
  • 基于位置恢复
    • 就是将某个起始时间的二进制日志导入数据库中,c从而跳过某个发生错误的时间点实现数据的恢复
  • 基于时间点的恢复

    • 使用基于时间点的恢复,可能会出现在一个时间点里既同时存在正确的操作又存在错误的操作,所以我们需要一种更为精确的恢复方式

    增量恢复的方法

    • 一般恢复
    • mysqlbinlog [--no-defaults] 增量备份文件 | mysql -u 用户名 -p
  • 基于位置的恢复
    • 恢复数据到指定位置
    • mysqlbinlog --stop-position=‘操作 id‘ 二进制日志 | mysql -u 用户名 -p 密码
    • 从指定的位置开始恢复数据
    • mysqlbinlog --start-position=‘操作 id‘ 二进制日志 | mysql -u 用户名 -p 密码
  • 基于时间点的恢复
    • 从日志开头截止到某个时间点的恢复
    • mysqlbinlog [-no-defaults] --stop-datetime=‘年-月-日 小时:分钟:秒‘ 二进制日志 | mysql -u 用户名-p 密码
    • 从某个时间点到日志结尾的恢复
    • mysqlbinlog [-no-defaults] --start-datetime=‘年-月-日 小时:分钟:秒‘ 二进制日志 | mysql -u 用户名 -p 密码
    • 从某个时间点到某个时间点的恢复
    • mysqlbinlog [-no-defaults] --start-datetime=‘年-月-日 小时:分钟:秒‘ --stop-datetime=‘年-月-日 小时:分钟:秒‘ 二进制日志 | mysql -u 用户名 -p 密码

操作实例

创建数据库、编辑表数据

[[email protected] ~]# mysql -uroot -p      //进入数据库
Enter password: 

mysql> create database school;      //创建数据库
Query OK, 1 row affected (0.01 sec)

mysql> use school;     //使用数据库
Database changed
mysql> create table info(       //创建表
        -> id int(3) not null primary key auto_increment,
        -> name varchar(10) not null,
        -> score decimal(4,1) not null);
Query OK, 0 rows affected (0.02 sec)

mysql> desc info;       //查看表结构
 ------- -------------- ------ ----- --------- ---------------- 
| Field | Type         | Null | Key | Default | Extra          |
 ------- -------------- ------ ----- --------- ---------------- 
| id    | int(3)       | NO   | PRI | NULL    | auto_increment |
| name  | varchar(10)  | NO   |     | NULL    |                |
| score | decimal(4,1) | NO   |     | NULL    |                |
 ------- -------------- ------ ----- --------- ---------------- 
3 rows in set (0.00 sec)

mysql> insert into info (name,score) values (‘stu01‘,88),(‘stu02‘,77);        //插入表数据
Query OK, 2 rows affected (0.02 sec)
Records: 2  Duplicates: 0  Warnings: 0

mysql> select * from info;      //查看表内容
 ---- ------- ------- 
| id | name  | score |
 ---- ------- ------- 
|  1 | stu01 |  88.0 |
|  2 | stu02 |  77.0 |
 ---- ------- ------- 
2 rows in set (0.01 sec)

mysql> select * from info limit 1;      //只显示表中的前1行
 ---- ------- ------- 
| id | name  | score |
 ---- ------- ------- 
|  1 | stu01 |  88.0 |
 ---- ------- ------- 
1 row in set (0.00 sec)

完整备份操作

[[email protected] ~]# cd /usr/local/mysql/data/     //切换到数据库的数据目录下
[[email protected] data]# ls
auto.cnf        ibdata1      ib_logfile1  mysql               school  test
ib_buffer_pool  ib_logfile0  ibtmp1       performance_schema  sys
[[email protected] data]# cd school/
[[email protected] school]# ls         //数据中的文件
db.opt  info.frm  info.ibd
[[email protected] school]# cd ..
[[email protected] data]# tar Jcvf /opt/mysql-$(date  %F).tar.xz /usr/local/mysql/data/    //用xz格式压缩
[[email protected] data]# cd /opt/
[[email protected] opt]# ls
mysql-2019-11-26.tar.xz  mysql-5.7.20  rh

单个数据库逻辑备份

[[email protected] opt]# mysqldump -uroot -p school > /opt/school.sql    //逻辑备份单个数据库
Enter password: 
[[email protected] opt]# ls
mysql-2019-11-26.tar.xz  mysql-5.7.20  rh  school.sql
[[email protected] opt]# vim school.sql        //查看备份数据库脚本

...
CREATE TABLE `info` (
    `id` int(3) NOT NULL AUTO_INCREMENT,
    `name` varchar(10) NOT NULL,
    `score` decimal(4,1) NOT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;
...
LOCK TABLES `info` WRITE;
/*!40000 ALTER TABLE `info` DISABLE KEYS */;
INSERT INTO `info` VALUES (1,‘stu01‘,88.0),(2,‘stu02‘,77.0);
...

多个数据库备份

[[email protected] opt]# mysqldump -uroot -p --databases school mysql > /opt/db_school_mysql.sql    //备份多个数据库
Enter password: 
[[email protected] opt]# ls
db_school_mysql.sql  mysql-2019-11-26.tar.xz  mysql-5.7.20  rh  school.sql

完全备份

[[email protected] opt]# mysqldump -uroot -p --opt --all-databases > /opt/all.sql    //完全备份
Enter password: 
[[email protected] opt]# ls
all.sql              mysql-2019-11-26.tar.xz  rh
db_school_mysql.sql  mysql-5.7.20             school.sql

数据库中的表进行备份

[[email protected] opt]# mysqldump -uroot -p school info > /opt/school_info.sql     //对数据库中的表进行备份
Enter password: 
[[email protected] opt]# ls
all.sql              mysql-2019-11-26.tar.xz  rh               school.sql
db_school_mysql.sql  mysql-5.7.20             school_info.sql

表结构备份

[[email protected] opt]# mysqldump -uroot -p -d school info > /opt/school_info_desc.sql    //对表结构进行备份
Enter password: 
[[email protected] opt]# ls
all.sql                  mysql-5.7.20          school_info.sql
db_school_mysql.sql      rh                    school.sql
mysql-2019-11-26.tar.xz  school_info_desc.sql

基于脚本恢复数据库

[[email protected] opt]# mysql -uroot -p     //进入数据库
Enter password: 

mysql> show databases;   //查看数据库
 -------------------- 
| Database           |
 -------------------- 
| information_schema |
| mysql              |
| performance_schema |
| school             |
| sys                |
| test               |
 -------------------- 
6 rows in set (0.00 sec)

mysql> use school;    //使用数据库
Database changed

mysql> show tables;   //查看表
 ------------------ 
| Tables_in_school |
 ------------------ 
| info             |
 ------------------ 
1 row in set (0.00 sec)

mysql> drop table info;    //删除表
Query OK, 0 rows affected (0.01 sec)

mysql> show tables;        //查看表
Empty set (0.00 sec)

mysql> source /opt/school.sql  //恢复数据库脚本文件

mysql> show tables;    //查看表
 ------------------ 
| Tables_in_school |
 ------------------ 
| info             |
 ------------------ 
1 row in set (0.00 sec)

基于外部MySQL命令恢复数据库

mysql> drop table info;   //删除表
Query OK, 0 rows affected (0.01 sec)

mysql> show tables;   //查看表
Empty set (0.00 sec)

mysql> quit   //退出
Bye
[[email protected] opt]# mysql -uroot -p123123 school < /opt/school.sql   //利用mysql命令进行恢复
mysql: [Warning] Using a password on the command line interface can be insecure.
[[email protected] opt]# mysql -uroot -p123123    //进入数据库
mysql: [Warning] Using a password on the command line interface can be insecure.

mysql> use school;    //使用数据库
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_school |
 ------------------ 
| info             |
 ------------------ 
1 row in set (0.00 sec)

MySQL增量备份及恢复

开启二进制日志文件

[[email protected] opt]# vim /etc/my.cnf  //开启二进制日志文件
[mysqld]
user = mysql
basedir = /usr/local/mysql
datadir = /usr/local/mysql/data
port = 3306
character_set_server=utf8
pid-file = /usr/local/mysql/mysql.pid
socket = /usr/local/mysql/mysql.sock
log-bin=mysql-bin   //开启二进制日志文件
server-id = 1
[[email protected] opt]# systemctl restart mysqld.service   //重启mysql服务
[[email protected] opt]# cd /usr/local/mysql/data/   //切换到mysql站点
[[email protected] data]# ls     //查看二进制日志文件
auto.cnf        ib_logfile0  mysql             performance_schema  test
ib_buffer_pool  ib_logfile1  mysql-bin.000001  school
ibdata1         ibtmp1       mysql-bin.index   sys

进行完全备份

[[email protected] data]# mysqldump -uroot -p123123 school > /opt/school.sql      //一次完全备份
mysqldump: [Warning] Using a password on the command line interface can be insecure.
[[email protected] data]# ls
auto.cnf        ib_logfile0  mysql             performance_schema  test
ib_buffer_pool  ib_logfile1  mysql-bin.000001  school
ibdata1         ibtmp1       mysql-bin.index   sys
[[email protected] data]# mysqladmin -uroot -p123123 flush-logs    //刷新二进制日志文件
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
[[email protected] data]# ls         //生成新的二进制日志文件,接下来的操作会保存在mysql-bin.000002中
auto.cnf        ib_logfile0  mysql             mysql-bin.index     sys
ib_buffer_pool  ib_logfile1  mysql-bin.000001  performance_schema  test
ibdata1         ibtmp1       mysql-bin.000002  school

模拟误操作

[[email protected] data]# mysql -uroot -p123123  ##进入数据库
mysql: [Warning] Using a password on the command line interface can be insecure.

mysql> use school;    ##使用数据库
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 info;      //查看表
 ---- ------ ------- 
| id | name | score |
 ---- ------ ------- 
|  1 | st01 |  88.0 |
|  2 | st02 |  77.0 |
 ---- ------ ------- 
2 rows in set (0.00 sec)

mysql> insert into info (name,score) values (‘by01‘,66);   //正确操作
Query OK, 1 row affected (0.00 sec)

mysql> select * from info;
 ---- ------ ------- 
| id | name | score |
 ---- ------ ------- 
|  1 | st01 |  88.0 |
|  2 | st02 |  77.0 |
|  3 | by01 |  66.0 |
 ---- ------ ------- 
3 rows in set (0.00 sec)

mysql> delete from info where name=‘st01‘;   //错误操作
Query OK, 1 row affected (0.00 sec)

mysql> insert into info (name,score) values (‘by02‘,99);      //正确操作
Query OK, 1 row affected (0.00 sec)

mysql> select * from info;
 ---- ------ ------- 
| id | name | score |
 ---- ------ ------- 
|  2 | st02 |  77.0 |
|  3 | by01 |  66.0 |
|  4 | by02 |  99.0 |
 ---- ------ ------- 
3 rows in set (0.00 sec)

[[email protected] data]# mysqladmin -uroot -p123123 flush-logs    //刷新二进制日志文件        
mysqladmin: [Warning] Using a password on the command line interface can be insecure.
[[email protected] data]# mysqlbinlog --no-defaults --base64-output=decode-rows -v mysql-bin.000002 > /opt/bak.txt           //用64位解码器查看二进制日志文件,并生成一个文件
[[email protected] data]# cd /opt/
[[email protected] opt]# ls
bak.txt  mysql-5.7.20  rh  school.sql
[[email protected] opt]# vim bak.txt    //查看二进制日志文件

# at 1084
#191127 20:14:01 server id 1  end_log_pos 1132 CRC32 0xdcc90eb5         Write_rows: table id 221 flags: STMT_END_F
### INSERT INTO `school`.`info`   //第一次正确操作的时间和位置
### SET
###   @1=3
###   @2=‘by01‘
###   @3=66.0
...
# at 1302   //停止位置点
#191127 20:14:46 server id 1  end_log_pos 1357 CRC32 0x6648509a         Table_map: `school`.`info` mapped to number 221
# at 1357
#191127 20:14:46 server id 1  end_log_pos 1405 CRC32 0x1eeb752b         Delete_rows: table id 221 flags: STMT_END_F
### DELETE FROM `school`.`info`   //第二次执行错误操作的时间和位置191127 20:14:46
### WHERE
###   @1=1
###   @2=‘st01‘
###   @3=88.0
# at 1405  //开始位置点
#191127 20:14:46 server id 1  end_log_pos 1436 CRC32 0xf1c8d903         Xid = 54
...
# at 1630
#191127 20:15:16 server id 1  end_log_pos 1678 CRC32 0x08d9b0f4         Write_rows: table id 221 flags: STMT_END_F
### INSERT INTO `school`.`info`   //第二次正确操作的时间和位置191127 20:15:16
### SET
###   @1=4
###   @2=‘by02‘
###   @3=99.0

基于时间点进行断点恢复

[[email protected] opt]# mysql -uroot -p123123   //进入数据库
mysql: [Warning] Using a password on the command line interface can be insecure.

mysql> use school;  //进入库
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> drop table info;   //删除数据库
Query OK, 0 rows affected (0.01 sec)

mysql> select * from info;   //查看表
ERROR 1146 (42S02): Table ‘school.info‘ doesn‘t exist
mysql> source /opt/school.sql   //恢复完全备份数据库脚本
...
mysql> show tables;   //查看表
 ------------------ 
| Tables_in_school |
 ------------------ 
| info             |
 ------------------ 
1 row in set (0.00 sec)

mysql> select * from info;  //查看表数据
 ---- ------ ------- 
| id | name | score |
 ---- ------ ------- 
|  1 | st01 |  88.0 |
|  2 | st02 |  77.0 |
 ---- ------ ------- 
2 rows in set (0.00 sec)

[[email protected] opt]# mysqlbinlog --no-defaults --stop-datetime=‘2019-11-27 20:14:46‘ /usr/local/mysql/data/mysql-bin.000002 | mysql -uroot -p123123          //恢复bin.000002中前一个正确的执行语句(从第二个错误语句时间点停止)
mysql: [Warning] Using a password on the command line interface can be insecure.
[[email protected] opt]# mysql -uroot -p123123      //进入数据库
mysql: [Warning] Using a password on the command line interface can be insecure.

mysql> use school;  //使用数据库
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 info;       //查看表数据,恢复了第一次正确操作
 ---- ------ ------- 
| id | name | score |
 ---- ------ ------- 
|  1 | st01 |  88.0 |
|  2 | st02 |  77.0 |
|  3 | by01 |  66.0 |
 ---- ------ ------- 
3 rows in set (0.00 sec)

[[email protected] opt]# mysqlbinlog --no-defaults --start-datetime=‘2019-11-27 20:15:16‘ /usr/local/mysql/data/mysql-bin.000002 | mysql -uroot -p123123        //跳过错误节点,恢复最后一个正确的操作(从最后一个正确的操作时间点开始)
mysql: [Warning] Using a password on the command line interface can be insecure.
[[email protected] opt]# mysql -uroot -p123123  //进入数据库
mysql: [Warning] Using a password on the command line interface can be insecure.

mysql> use school;   //使用数据库
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 info;      //查看表数据,恢复了第二次正确操作,跳过了错误的操作
 ---- ------ ------- 
| id | name | score |
 ---- ------ ------- 
|  1 | st01 |  88.0 |
|  2 | st02 |  77.0 |
|  3 | by01 |  66.0 |
|  4 | by02 |  99.0 |
 ---- ------ ------- 
4 rows in set (0.00 sec)

基于位置点进行断点恢复

mysql> delete from info where name=‘by01‘;    //为实验方便直接删除
Query OK, 1 row affected (0.01 sec)

mysql> delete from info where name=‘by02‘;    //删除
Query OK, 1 row affected (0.00 sec)

mysql> select * from info;    //完全备份的初始状态
 ---- ------ ------- 
| id | name | score |
 ---- ------ ------- 
|  1 | st01 |  88.0 |
|  2 | st02 |  77.0 |
 ---- ------ ------- 
2 rows in set (0.00 sec)

mysql> quit
Bye

[[email protected] opt]# mysqlbinlog --no-defaults --stop-position=‘1302‘ /usr/local/mysql/data/mysql-bin.000002 | mysql -uroot -p123123                 //跳过错误操作的位置点从上一个位置点开始
[[email protected] opt]# mysql -uroot -p123123   //进入数据库
mysql: [Warning] Using a password on the command line interface can be insecure.

mysql> use school;    //使用数据库
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 info;   //查看表数据,恢复了第一次正确的操作
 ---- ------ ------- 
| id | name | score |
 ---- ------ ------- 
|  1 | st01 |  88.0 |
|  2 | st02 |  77.0 |
|  3 | by01 |  66.0 |
 ---- ------ ------- 
3 rows in set (0.00 sec)

mysql> quit
Bye

[[email protected] opt]# mysqlbinlog --no-defaults --start-position=‘1405‘ /usr/local/mysql/data/mysql-bin.000002 | mysql -uroot -p123123      //从错误的位置后一个位置点开始,跳过错误操作的位置点
[[email protected] opt]# mysql -uroot -p123123   //进入数据库
mysql: [Warning] Using a password on the command line interface can be insecure.

mysql> use school;   ##使用数据库
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 info;    //查看表数据,跳过错误操作,恢复第二次正确操作数据
 ---- ------ ------- 
| id | name | score |
 ---- ------ ------- 
|  1 | st01 |  88.0 |
|  2 | st02 |  77.0 |
|  3 | by01 |  66.0 |
|  4 | by02 |  99.0 |
 ---- ------ ------- 
4 rows in set (0.00 sec)

增量备份全部恢复

[[email protected] opt]# mysqlbinlog --no-defaults  /usr/local/mysql/data/mysql-bin.000002 | mysql -uroot -p123123       //全部增量恢复