备份的主要目的是灾难恢复,备份还可以测试应用、回滚数据修改、查询历史数据、审 计等。
在企业中数据的价值至关重要,数据保障了企业业务的正常运行。因此,数据的安全性 及数据的可靠性是运维的重中之重,任何数据的丢失都可能对企业产生严重的后果。通常情 况下造成数据丢失的原因有如下几种:
程序错误。
人为操作错误。
运算错误。
磁盘故障。
灾难(如火灾、地震)和盗窃。
数据库备份可以分为物理备份和逻辑备份。物理备份是对数据库操作系统的物理文件 (如数据文件、日志文件等)的备份。这种类型的备份适用于在出现问题时需要快速恢复的 大型重要数据库。 物理备份又可以分为冷备份(脱机备份)、热备份(联机备份)和温备份。
从数据库的备份策略角度,数据库的备份可分为完全备份、差异备份和增量备份
MySQL 数据库的备份可以采用很多种方式,如直接打包数据库文件(物理冷备份)、 专用备份工具(mysqldump)、二进制日志增量备份、第三方工具备份等。
物理冷备份一般用 tar 命令直接打包数据库文件夹,而在进行备份之前需要使用 “systemctl stop mysqld”命令关闭 mysqld 服务。
[root@server ~]# systemctl stop mysqld
[root@server ~]# mkdir /backup
[root@server ~]# tar zcfP /backup/mysql_all-$(date +%F).tar.gz /usr/local/mysql/data
[root@server ~]# cd /backup/
[root@server backup]# ls
mysql_all-2020-08-30.tar.gz
[root@server backup]# mkdir /bak
[root@server backup]# mv /usr/local/mysql/data/ /bak
[root@server backup]# tar zxvfP mysql_all-2020-08-30.tar.gz
[root@server backup]# systemctl start mysqld
[root@server backup]# netstat -antp | grep 3306
tcp6 0 0 :::3306 :::* LISTEN 8646/mysqld
通过 mysqldump 命令可以将指定的库、表或全部的库导出为 SQL 脚本,便于该命令 在不同版本的 MySQL 服务器上使用。例如,当需要升级 MySQL 服务器时,可以先使用 mysqldump 命令将原有库信息导出,然后直接在升级后的 MySQL 服务器中导入即可
使用 mysqldump 命令导出数据时,默认会直接在终端显示,若要保存到文件,还需要 结合 Shell 的“>”重定向输出操作,命令格式如下所示
语法:
备份指定库中的部分表:
mysqldump [选项] 库名 [表名 1] [表名 2] … > /备份路径/备份文件名
备份一个或多个完整的库(包括其中所有的表)
mysqldump [选项] 库名 [表名 1] [表名 2] … > /备份路径/备份文件名
备份 MySQL 服务器中所有的库。
mysqldump [选项] --all-databases > /备份路径/备份文件名
其中,常用的选项包括“-u”、“-p”,分别用于指定数据库用户名、密码
备份school库中的info表
[root@server backup]# mysqldump -uroot -p school info > mysqlinfo.sql
Enter password:
[root@server backup]# ls
mysql_all-2020-08-30.tar.gz mysqlinfo.sql
备份school库
[root@server backup]# mysqldump -uroot -p --databases school > school.sql
Enter password:
[root@server backup]# ls
mysql_all-2020-08-30.tar.gz mysqlinfo.sql school.sql
若需要备份整个 MySQL 服务器中的所有库,应使用格式 3。当导出的数据量较大的时 候,可以添加“–opt”选项以优化执行速度。
备份所有数据库
[root@server backup]# mysqldump -uroot -p --opt --all-databases > all-database.sql
Enter password:
[root@server backup]# ls
all-database.sql mysql_all-2020-08-30.tar.gz mysqlinfo.sql school.sql
备份多个库,中间用空格分隔,例如备份school和class库:
mysqldump -uroot -p --databases school class > school.sql
使用 mysqldump 命令导出的 SQL 备份脚本,在需要恢复时可以通过 mysql 命令对其进行导入操作
语法:
mysql [选项] [库名] [表名] < /备份路径/备份文件名
当备份文件中只包含表的备份,而不包含创建的库的语句时,执行导入操作时必须指定 库名,且目标库必须存在
先创建schoolbak库,然后把原来school的info表恢复到schoolbak库中,-e表示在mysql里执行的语句,语句用引号引起来
mysql> create database schoolbak;
Query OK, 1 row affected (0.00 sec)
[root@server backup]# mysql -u root -p schoolbak < mysqlinfo.sql
Enter password:
[root@server backup]# mysql -u root -p -e 'show tables from schoolbak;'
Enter password:
+---------------------+
| Tables_in_schoolbak |
+---------------------+
| info |
+---------------------+
若备份文件中已经包括完整的库信息,则执行导入操作时无须指定库名
恢复school库
mysql> drop database school;
Query OK, 8 rows affected (0.02 sec)
[root@server backup]# mysql -uroot -p < school.sql
Enter password:
mysql> show databases;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
| school |
| school2 |
| schoolbak |
| sys |
+--------------------+
7 rows in set (0.00 sec)
使用 mysqldump 进行完全备份,备份的数据中有重复数据,备份时间与恢复时间过长。 而增量备份就是自上一次备份之后增加或改变的内容
与完全备份不同,增量备份没有重复数据,备份量不大,时间短;但其恢复麻烦,需 要上次完全备份及完全备份之后所有的增量备份才能恢复,而且要对所有增量备份进行逐 个反推恢复。MySQL 没有提供直接的增量备份办法,可以通过 MySQL 提供的二进制日志 (binary logs)间接实现增量备份
二进制日志保存了所有更新数据库的操作。二进制日志在启动 MySQL 服务器后开始记 录,并在文件达到二进制日志所设置的最大值或者接收到 flush logs 命令后重新创建新的日志文件,生成二进制文件序列,并及时把这些日志保存到安全的存储位置,即可完成一个时 间段的增量备份。使用 max_binlog_size 配置项可以设置二进制日志文件的最大值,如果二进制文件的大小超过了 max_binlog_size,它就会自动创建新的二进制文件。
要进行 MySQL 的增量备份,首先要开启二进制日志功能。开启 MySQL 的二进制日志 功能的实现方法有很多种,最常用的是在 MySQL 配置文件的 mysqld 项下加入“log-bin=/ 文件路径/文件名”前缀,如 log-bin=/usr/local/mysql/mysql-bin,然后重启 MySQL 服务就可以在指定路径下查看二进制日志文件了。默认情况下,二进制日志文件的扩展名是一个六位的数字,如 mysql-bin.000001。
进入mysql配置文件,加入log-bin=master-bin开启二进制文件,名字的前缀自己定义,这里叫master-bin,配置完成后需要重启mysql服务,重启之后默认会产生第一个二进制日志:master-bin.000001
[root@server data]# vim /etc/my.cnf
[mysqld]
log-bin=master-bin
[root@server data]# systemctl restart mysqld
[root@server data]# ls
auto.cnf ib_logfile1 mysql schoolbak
ib_buffer_pool ibtmp1 performance_schema sys
ibdata1 master-bin.000001 school
ib_logfile0 master-bin.index school2
常用的增量恢复的方法有三种:一般恢复、基于位置的恢复、基于时间点的恢复
语法:
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 密码
查看info表的信息
mysql> select * from info;
+----+-----------+-------+----------+-------+
| id | name | score | address | hobby |
+----+-----------+-------+----------+-------+
| 1 | shidapeng | 90.00 | nanjing | 2 |
| 2 | shangzhen | 80.00 | beijing | 1 |
| 3 | tangyan | 98.00 | shanghai | 1 |
| 6 | chengu | 88.00 | nanjing | 1 |
| 7 | caicai | 70.00 | hangzhou | 1 |
| 8 | zhaokun | 80.00 | hangzhou | 2 |
| 9 | xiawenjie | 80.00 | hangzhou | 1 |
| 10 | zhaobin | NULL | shanghai | 1 |
| 11 | nannan | 76.00 | | 2 |
+----+-----------+-------+----------+-------+
9 rows in set (0.00 sec)
先进行一次完全备份,以当前日期命名
一次完全备份之后刷新日志,表示切割日志,这时会出现第二个二进制日志master-bin.000002
[root@server data]# mysqldump -uroot -p school info > info-$(date +%F).sql
Enter password:
[root@server data]# mysqladmin -uroot -p flush-logs <---刷新日志
Enter password:
[root@server data]# ls
auto.cnf ibdata1 ib_logfile1 info-2020-08-31.sql master-bin.000002 mysql school schoolbak
ib_buffer_pool ib_logfile0 ibtmp1 master-bin.000001 master-bin.index performance_schema school2 sys
在info表中插入两条记录学生chenqiang和hewei
mysql> insert into info (name,score,address,hobby) values('chenqiang',65,'nanjing',2);
Query OK, 1 row affected (0.00 sec)
mysql> insert into info (name,score,address,hobby) values('hewei',85,'shanghai',1);
Query OK, 1 row affected (0.00 sec)
mysql> select * from info;
+----+-----------+-------+----------+-------+
| id | name | score | address | hobby |
+----+-----------+-------+----------+-------+
| 1 | shidapeng | 90.00 | nanjing | 2 |
| 2 | shangzhen | 80.00 | beijing | 1 |
| 3 | tangyan | 98.00 | shanghai | 1 |
| 6 | chengu | 88.00 | nanjing | 1 |
| 7 | caicai | 70.00 | hangzhou | 1 |
| 8 | zhaokun | 80.00 | hangzhou | 2 |
| 9 | xiawenjie | 80.00 | hangzhou | 1 |
| 10 | zhaobin | NULL | shanghai | 1 |
| 11 | nannan | 76.00 | | 2 |
| 12 | chenqiang | 65.00 | nanjing | 2 |
| 13 | hewei | 85.00 | shanghai | 1 |
+----+-----------+-------+----------+-------+
11 rows in set (0.00 sec)
这个时候再次刷新日志,会产生一个新的日志master-bin.000003,而之前插入的两条记录保存在了master-bin.000002日志文件里
[root@server data]# mysqladmin -uroot -p flush-logs
Enter password:
[root@server data]# ls
auto.cnf ib_logfile0 info-2020-08-31.sql master-bin.000003 performance_schema schoolbak
ib_buffer_pool ib_logfile1 master-bin.000001 master-bin.index school sys
ibdata1 ibtmp1 master-bin.000002 mysql school2
首先删除表模拟故障
mysql> drop table info;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from info;
ERROR 1146 (42S02): Table 'school.info' doesn't exist
把之前完全备份的sql文件导入到数据库中,可以看到恢复到了插入学生chenqiang和hewei之前的记录
[root@server data]# mysql -uroot -p school < info-2020-08-31.sql
Enter password:
mysql> select * from info;
+----+-----------+-------+----------+-------+
| id | name | score | address | hobby |
+----+-----------+-------+----------+-------+
| 1 | shidapeng | 90.00 | nanjing | 2 |
| 2 | shangzhen | 80.00 | beijing | 1 |
| 3 | tangyan | 98.00 | shanghai | 1 |
| 6 | chengu | 88.00 | nanjing | 1 |
| 7 | caicai | 70.00 | hangzhou | 1 |
| 8 | zhaokun | 80.00 | hangzhou | 2 |
| 9 | xiawenjie | 80.00 | hangzhou | 1 |
| 10 | zhaobin | NULL | shanghai | 1 |
| 11 | nannan | 76.00 | | 2 |
+----+-----------+-------+----------+-------+
9 rows in set (0.00 sec)
进行增量备份恢复,学生chenqiang和何威恢复了
[root@server data]# mysqlbinlog --no-defaults /usr/local/mysql/data/master-bin.000002 | mysql -uroot -p
Enter password:
mysql> select * from info;
+----+-----------+-------+----------+-------+
| id | name | score | address | hobby |
+----+-----------+-------+----------+-------+
| 1 | shidapeng | 90.00 | nanjing | 2 |
| 2 | shangzhen | 80.00 | beijing | 1 |
| 3 | tangyan | 98.00 | shanghai | 1 |
| 6 | chengu | 88.00 | nanjing | 1 |
| 7 | caicai | 70.00 | hangzhou | 1 |
| 8 | zhaokun | 80.00 | hangzhou | 2 |
| 9 | xiawenjie | 80.00 | hangzhou | 1 |
| 10 | zhaobin | NULL | shanghai | 1 |
| 11 | nannan | 76.00 | | 2 |
| 12 | chenqiang | 65.00 | nanjing | 2 |
| 13 | hewei | 85.00 | shanghai | 1 |
+----+-----------+-------+----------+-------+
11 rows in set (0.00 sec)
通过查看二进制日志文件的具体内容可以发现,在每进行一个操作之前都会有一个独特的编号, 如“# at 293”。此编号随着操作数增多而变大,我们称之为操作 ID。在操作 ID 下面紧跟着的是时间标记。要实现基于位置或时间点恢复数据,需要分别依赖二进制日志文件中的操作 ID 或者时间标记。
但是master-bin.000002文件是二进制文件我们是读不懂的,需要使用以下命令进行解码再打开就可以看到具体数据库操作了
[root@server data]# mysqlbinlog --no-defaults --base64-output=decode-rows -v master-bin.000002 > /bak/logbak
[root@server data]# vim /bak/logbak
# at 293
#200831 15:40:58 server id 11 end_log_pos 352 CRC32 0x22e316cd Table_map: `school`.`info` mapped to number 120
# at 352
#200831 15:40:58 server id 11 end_log_pos 417 CRC32 0x89368e75 Write_rows: table id 120 flags: STMT_END_F
### INSERT INTO `school`.`info`
### SET
### @1=12
### @2='chenqiang'
### @3=65.00
### @4='nanjing'
### @5=2
# at 587
#200831 15:41:23 server id 11 end_log_pos 646 CRC32 0x30e4d1ca Table_map: `school`.`info` mapped to number 120
# at 646
#200831 15:41:23 server id 11 end_log_pos 708 CRC32 0xbb3a1102 Write_rows: table id 120 flags: STMT_END_F
### INSERT INTO `school`.`info`
### SET
### @1=13
### @2='hewei'
### @3=85.00
### @4='shanghai'
### @5=1
依旧删除表模拟故障,然后做一次完全备份恢复
mysql> drop table info;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from info;
ERROR 1146 (42S02): Table 'school.info' doesn't exist
[root@server data]# mysql -uroot -p school < info-2020-08-31.sql
Enter password:
mysql> select * from info;
+----+-----------+-------+----------+-------+
| id | name | score | address | hobby |
+----+-----------+-------+----------+-------+
| 1 | shidapeng | 90.00 | nanjing | 2 |
| 2 | shangzhen | 80.00 | beijing | 1 |
| 3 | tangyan | 98.00 | shanghai | 1 |
| 6 | chengu | 88.00 | nanjing | 1 |
| 7 | caicai | 70.00 | hangzhou | 1 |
| 8 | zhaokun | 80.00 | hangzhou | 2 |
| 9 | xiawenjie | 80.00 | hangzhou | 1 |
| 10 | zhaobin | NULL | shanghai | 1 |
| 11 | nannan | 76.00 | | 2 |
+----+-----------+-------+----------+-------+
9 rows in set (0.00 sec)
位置点587那里是创建学生hewei的位置点,所以定位到这个位置点就停止恢复,所以学生chenqiang恢复了,hewei没有恢复
[root@server bak]# mysqlbinlog --no-defaults --stop-position='587' /usr/local/mysql/data/master-bin.000002 | mysql -uroot -p
Enter password:
mysql> select * from info;
+----+-----------+-------+----------+-------+
| id | name | score | address | hobby |
+----+-----------+-------+----------+-------+
| 1 | shidapeng | 90.00 | nanjing | 2 |
| 2 | shangzhen | 80.00 | beijing | 1 |
| 3 | tangyan | 98.00 | shanghai | 1 |
| 6 | chengu | 88.00 | nanjing | 1 |
| 7 | caicai | 70.00 | hangzhou | 1 |
| 8 | zhaokun | 80.00 | hangzhou | 2 |
| 9 | xiawenjie | 80.00 | hangzhou | 1 |
| 10 | zhaobin | NULL | shanghai | 1 |
| 11 | nannan | 76.00 | | 2 |
| 12 | chenqiang | 65.00 | nanjing | 2 |
+----+-----------+-------+----------+-------+
10 rows in set (0.00 sec)
依旧删除表模拟故障,然后做一次完全备份恢复
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
[root@server data]# mysql -uroot -p school < info-2020-08-31.sql
Enter password:
mysql> select * from info;
+----+-----------+-------+----------+-------+
| id | name | score | address | hobby |
+----+-----------+-------+----------+-------+
| 1 | shidapeng | 90.00 | nanjing | 2 |
| 2 | shangzhen | 80.00 | beijing | 1 |
| 3 | tangyan | 98.00 | shanghai | 1 |
| 6 | chengu | 88.00 | nanjing | 1 |
| 7 | caicai | 70.00 | hangzhou | 1 |
| 8 | zhaokun | 80.00 | hangzhou | 2 |
| 9 | xiawenjie | 80.00 | hangzhou | 1 |
| 10 | zhaobin | NULL | shanghai | 1 |
| 11 | nannan | 76.00 | | 2 |
+----+-----------+-------+----------+-------+
9 rows in set (0.00 sec)
如果要恢复学生hewei的记录,需要定位开始的位置点为587,因为恢复从这里开始,所以前面的操作都不会被恢复
[root@server bak]# mysqlbinlog --no-defaults --start-position='587' /usr/local/mysql/data/master-bin.000002 | mysql -uroot -p
Enter password:
mysql> select * from info;
+----+-----------+-------+----------+-------+
| id | name | score | address | hobby |
+----+-----------+-------+----------+-------+
| 1 | shidapeng | 90.00 | nanjing | 2 |
| 2 | shangzhen | 80.00 | beijing | 1 |
| 3 | tangyan | 98.00 | shanghai | 1 |
| 6 | chengu | 88.00 | nanjing | 1 |
| 7 | caicai | 70.00 | hangzhou | 1 |
| 8 | zhaokun | 80.00 | hangzhou | 2 |
| 9 | xiawenjie | 80.00 | hangzhou | 1 |
| 10 | zhaobin | NULL | shanghai | 1 |
| 11 | nannan | 76.00 | | 2 |
| 13 | hewei | 85.00 | shanghai | 1 |
+----+-----------+-------+----------+-------+
10 rows in set (0.00 sec)
基于时间点的数据恢复所使用的选项是“–stop-datetime”,指定的时间同样也是查询二进制日志所得
依然是删除表模拟故障然后进行一次完全备份恢复
mysql> drop table info;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from info;
ERROR 1146 (42S02): Table 'school.info' doesn't exist
[root@server data]# mysql -uroot -p school < info-2020-08-31.sql
Enter password:
mysql> select * from info;
+----+-----------+-------+----------+-------+
| id | name | score | address | hobby |
+----+-----------+-------+----------+-------+
| 1 | shidapeng | 90.00 | nanjing | 2 |
| 2 | shangzhen | 80.00 | beijing | 1 |
| 3 | tangyan | 98.00 | shanghai | 1 |
| 6 | chengu | 88.00 | nanjing | 1 |
| 7 | caicai | 70.00 | hangzhou | 1 |
| 8 | zhaokun | 80.00 | hangzhou | 2 |
| 9 | xiawenjie | 80.00 | hangzhou | 1 |
| 10 | zhaobin | NULL | shanghai | 1 |
| 11 | nannan | 76.00 | | 2 |
+----+-----------+-------+----------+-------+
9 rows in set (0.00 sec)
设置停止时间点2020-08-31 15:41:23,注意时间的格式,跟之前按位置恢复的原理一样,只是这里换成了时间
[root@server bak]# mysqlbinlog --no-defaults --stop-datetime='2020-08-31 15:41:23' /usr/local/mysql/data/master-bin.000002 | mysql -uroot -p
Enter password:
mysql> select * from info;
+----+-----------+-------+----------+-------+
| id | name | score | address | hobby |
+----+-----------+-------+----------+-------+
| 1 | shidapeng | 90.00 | nanjing | 2 |
| 2 | shangzhen | 80.00 | beijing | 1 |
| 3 | tangyan | 98.00 | shanghai | 1 |
| 6 | chengu | 88.00 | nanjing | 1 |
| 7 | caicai | 70.00 | hangzhou | 1 |
| 8 | zhaokun | 80.00 | hangzhou | 2 |
| 9 | xiawenjie | 80.00 | hangzhou | 1 |
| 10 | zhaobin | NULL | shanghai | 1 |
| 11 | nannan | 76.00 | | 2 |
| 12 | chenqiang | 65.00 | nanjing | 2 |
+----+-----------+-------+----------+-------+
10 rows in set (0.00 sec)
删除表模拟故障,然后进行一次完全备份恢复
mysql> drop table info;
Query OK, 0 rows affected (0.00 sec)
mysql> select * from info;
ERROR 1146 (42S02): Table 'school.info' doesn't exist
[root@server data]# mysql -uroot -p school < info-2020-08-31.sql
Enter password:
mysql> select * from info;
+----+-----------+-------+----------+-------+
| id | name | score | address | hobby |
+----+-----------+-------+----------+-------+
| 1 | shidapeng | 90.00 | nanjing | 2 |
| 2 | shangzhen | 80.00 | beijing | 1 |
| 3 | tangyan | 98.00 | shanghai | 1 |
| 6 | chengu | 88.00 | nanjing | 1 |
| 7 | caicai | 70.00 | hangzhou | 1 |
| 8 | zhaokun | 80.00 | hangzhou | 2 |
| 9 | xiawenjie | 80.00 | hangzhou | 1 |
| 10 | zhaobin | NULL | shanghai | 1 |
| 11 | nannan | 76.00 | | 2 |
+----+-----------+-------+----------+-------+
9 rows in set (0.00 sec)
设置开始恢复的时间点2020-08-31 15:41:23
[root@server bak]# mysqlbinlog --no-defaults --start-datetime='2020-08-31 15:41:23' /usr/local/mysql/data/master-bin.000002 | mysql -uroot -p
Enter password:
mysql> select * from info;
+----+-----------+-------+----------+-------+
| id | name | score | address | hobby |
+----+-----------+-------+----------+-------+
| 1 | shidapeng | 90.00 | nanjing | 2 |
| 2 | shangzhen | 80.00 | beijing | 1 |
| 3 | tangyan | 98.00 | shanghai | 1 |
| 6 | chengu | 88.00 | nanjing | 1 |
| 7 | caicai | 70.00 | hangzhou | 1 |
| 8 | zhaokun | 80.00 | hangzhou | 2 |
| 9 | xiawenjie | 80.00 | hangzhou | 1 |
| 10 | zhaobin | NULL | shanghai | 1 |
| 11 | nannan | 76.00 | | 2 |
| 13 | hewei | 85.00 | shanghai | 1 |
+----+-----------+-------+----------+-------+
10 rows in set (0.00 sec)