MySQL 备份与恢复
1. 实验介绍
1.1 实验内容
在本节内容中,我们将学习 MySQL 备份与恢复的一些相关知识
1.2 实验知识点
- 导出表的数据
- mysqldump
- 二进制日志备份
2. 概述
2.1 方法介绍
在第一周 Linux 相关内容的学习中,我们有提到过备份的一些知识。
在大多数时候,对于一个Linux 系统来讲,并不是所有的内容都是需要备份的。有些时候我们只需要对于一些关键数据进行备份。而对于需要备份的数据可能会根据实际的情况有所不同,例如,当前系统主要提供的是数据库存储等服务,那么重要的就是你的数据库文件,以及一些重要的配置信息。
而对于这里的数据库备份来说,MySQL 有自己的一些备份的方法。这里我们简要介绍 MySQL 的几种备份方法:
- 直接复制数据文件
- 使用导出表数据到一个文件中的方式
- 使用客户端工具,mysqldump
- 使用二进制日志进行备份
2.2 创建示例表和数据库
在开始学习备份相关的操作时,我们需要创建相应的示例表和数据库。这里直接使用前面的选课数据库用作演示示例。相应的创建步骤可以参考本周第五个实验,数据的搜索部分的相关知识。
3. 数据目录
我们知道 MySQL 的配置文件是存放在 /etc/mysql/ 目录下,而它的数据保存路径则为 /var/lib/mysql/。该路径在 /etc/mysql/my.cnf 配置文件中有相应的定义:
因此可以直接复制整个 /var/lib/mysql 目录来备份数据库。
首先,我们复制该文件夹到当前目录下:
$ sudo cp -rf /var/lib/mysql/ ~/mysql
这时我们手动删除 shiyanlou001 数据库,使用如下语句:
$ mysql -u root -p -e "DROP DATABASE IF EXISTS shiyanlou001;"
删除后,再次查看 shiyanlou001 数据库就已经不存在了。我们如果需要恢复 shiyanlou001 数据库,可以直接将当前目录下的 mysql 文件夹来替换复制到 /var/lib/mysql 目录,使用如下命令:
$ sudo rm -r /var/lib/mysql
$ sudo cp -rf ~/mysql /var/lib/mysql
命令执行成功后,该数据中的数据为我们在执行删除 shiyanlou001 数据库之前的内容,但是此时依旧不能正确使用该数据库,因为我们复制时使用的是 sudo,即该目录所属的用户和用户组都为 root,需要将其修改为 mysql,使用如下命令:
$ sudo chown -R mysql:mysql /var/lib/mysql
$ sudo service mysql restart
该命令执行成功后,就可以正确的使用该数据库了。例如我们通过如下语句,查看 shiyanlou001 数据库中的表:
$ mysql -u root -p -e "USE shiyanlou001;SHOW TABLES;"
4. 导出表数据
4.1 导出
在数据的搜索一节中我们介绍了很多 SELECT 的使用方式。除了之前学习过的使用方法,我们还可以使用 SELECT... INTO 的形式将查询的结果写入到文件中。通过这样的方式来达到备份的效果。
需要注意的是,写入的文件在写入时不能已经存在
详细的语法格式如下:
SELECT ... INTO OUTFILE 'file_name' [CHARACTER SET charset_name] export_options
或者使用
SELECT ... INTO DUMPFILE 'file_name'
- 第一条语句可以将选定的行,即查询到的行写入文件。并且可以通过 export_options,即导出配置,指定输出格式。
- 第二条语句,导出的文件中没有任何的格式。
例如,我们导出 student 数据表到 student.txt 文件中,就可以使用如下语句:
mysql> USE shiyanlopu001;
mysql> SELECT * FROM student INTO OUTFILE "student.txt" CHARACTER SET utf8;
上述命令会在 /var/lib/mysql/shiyanlou001/ 目录下生成一个 student.txt 文件。保存的路径会受到环境变量 secure_file_priv 的影响,我们直接查看该变量的值为:
mysql> SHOW VARIABLES LIKE "secure_file%";
+------------------+--------+
| Variable_name | Value |
+------------------+--------+
| secure_file_priv | |
+------------------+--------+
该变量的值为空,所以这里会保存到当前所使用的数据库的数据目录下。这里需要着重强调的是,该文件不应放在 /var/lib/mysql/shiyanlou001/ 目录下,会影响我们对于 shiyanlou001 数据库的一些操作,例如,我们通过 sql 语句删除 shiyanlou001 数据库时就会因为无法删除该文件而提示错误。
而在 MySQL 5.5.53 之后的版本中,该变量的值变为 /var/lib/mysql-files/,导出文件路径请使用完整路径,比如“/var/lib/mysql-files/student.txt”。实验环境中的 MySQL 版本为 5.5.50。
所以这里我们需要删除掉刚刚生成的 student.txt 文件,并修改 MySQL 的配置文件以设置 secure_file_priv 的值:
- 删除文件
$ sudo rm /var/lib/mysql/shiyanlou001/student.txt
- 修改 /etc/mysql/my.cnf 配置文件,并在其中加入一行内容:
[mysqld]
...
secure_file_priv = /var/lib/mysql-files
注意需要手动创建这个目录 /var/lib/mysql-files,并使用 chown 设置目录的所有者为 mysql 用户才可以启动 MySQL 服务器。
sudo mkdir /var/lib/mysql-files
sudo chown mysql.root -R /var/lib/mysql-files
- 重启 MySQL
$ sudo service mysql restart
如上所示,上述操作执行成功后,可以通过如下语句查看该变量设置的值:
mysql> SHOW VARIABLES LIKE "secure_file%";
+------------------+-----------------------+
| Variable_name | Value |
+------------------+-----------------------+
| secure_file_priv | /var/lib/mysql-files/ |
+------------------+-----------------------+
该变量的值为 /var/lib/mysql-files/,所以我们在进行导出的时候,需要修改相应的路径:
mysql> SELECT * FROM student INTO OUTFILE "/var/lib/mysql-files/student.txt" CHARACTER SET utf8;
Query OK, 5 rows affected (0.01 sec)
导出成功后,查看 /var/lib/mysql-files/student.txt 可以看到表中的数据被导出,使用如下命令:
$ sudo cat /var/lib/mysql-files/student.txt
该文件的内容按照一定的格式被导出,而这种格式我们也可以通过一些配置项来进行设置。即语法格式中的 export_options,它们的使用方式如下:
[FIELDS
[TERMINATED BY 'string']
[[OPTIONALLY] ENCLOSED BY 'char']
[ESCAPED BY 'char']
]
[LINES
[STARTING BY 'string']
[TERMINATED BY 'string']
]
上述语法中描述了两个可选的子句,即 FIELDS 和 LINES 子句。
首先是 FIELDS 子句的三个可配置项,它们代表的意思如下:
- TERMINATED BY 'string'] 用来指定字段之间的分隔符,例如我们指定为逗号(,),则导出的格式类似于 csv 格式。
- [[OPTIONALLY] ENCLOSED BY 'char'] 可以指定字符值使用什么字符包裹,例如使用双引号('"'),则字符值的数据都会使用双引号引起来。
- [ESCAPED BY 'char'] 用来指定转义字符,默认值为斜杠符号 \。
如下示例,我们使用上述的三个可选项:
# 设定以逗号为分隔符,字符值使用双引号引起来,并设定转义字符为 # ,保存到 student1.txt 文件中
mysql> SELECT * FROM student INTO OUTFILE "/var/lib/mysql-files/student1.txt" CHARACTER SET utf8 FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' ESCAPED BY '#';
Query OK, 5 rows affected (0.00 sec)
这时我们查看该文件的值,可以看到对应的修改:
$ sudo cat /var/lib/mysql-files/student1.txt
除了 FIELDS 子句,还有 LINES 子句,其两个可选的配置项如下所示:
- [STARTING BY 'string'] 指定一行开始的标识字符串。
- [TERMINATED BY 'string'] 指定一行结束的标识字符串。
如下示例,分别指定开始字符串为 '##',结束的字符串为 '####':
mysql> SELECT * FROM student INTO OUTFILE "/var/lib/mysql-files/student2.txt" CHARACTER SET utf8 FIELDS TERMINATED BY ',' OPTIONALLY ENCLOSED BY '"' ESCAPED BY '#' LINES STARTING BY '##' TERMINATED BY '####';
查看文件的内容如下图所示:
$ sudo cat /var/lib/mysql-files/student2.txt
4.2 导入
导出数据之后,如果需要导入数据,则可以使用 LOAD DATA ... INFILE 语句,相关的语法格式如下:
LOAD DATA INFILE 'file_name'
INTO TABLE tbl_name
[CHARACTER SET charset_name]
[{FIELDS | COLUMNS}
[TERMINATED BY 'string']
[[OPTIONALLY] ENCLOSED BY 'char']
[ESCAPED BY 'char']
]
[LINES
[STARTING BY 'string']
[TERMINATED BY 'string']
]
[(col_name, col_name ...)]
上述语法中的可选项 (col_name, col_name ...) 用于选择将要导入的部分列,因为导入与导出的数据有时并不一致。
需要注意的是,在导出表的时候,我们只导出了其中的数据,但是对于表结构的定义我们并没有进行导出。所以在进行导入的时候,表结构需要自己定义,即该表需要提前创建。
例如,我们将该文件的数据导入到 import_student 表中,首先需要创建该表:
mysql> USE shiyanlou001;
Database changed
mysql> CREATE TABLE import_student(
-> s_id INT,
-> s_name VARCHAR(20) NOT NULL,
-> s_sex ENUM("man","woman") DEFAULT "man",
-> s_age INT NOT NULL,
-> PRIMARY KEY (s_id)
-> ) DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.09 sec)
mysql>
创建成功后,我们执行导入语句,将 student.txt 文件中内容导入到 import_student 表中,并查看导入的数据:
mysql> LOAD DATA INFILE "/var/lib/mysql-files/student.txt" INTO TABLE import_student;
Query OK, 5 rows affected (0.03 sec)
Records: 5 Deleted: 0 Skipped: 0 Warnings: 0
mysql>
mysql> select * from import_student;
+------+---------------+-------+-------+
| s_id | s_name | s_sex | s_age |
+------+---------------+-------+-------+
| 1001 | shiyanlou1001 | man | 10 |
| 1002 | shiyanlou1002 | woman | 20 |
| 1003 | shiyanlou1003 | man | 18 |
| 1004 | shiyanlou1004 | woman | 40 |
| 1005 | shiyanlou1005 | man | 17 |
+------+---------------+-------+-------+
5 rows in set (0.00 sec)
如上所示,导入成功。
数据库导出和导入操作视频:
5. mysqldump
MySQL 提供了很多的客户端程序和工具用于各种辅助操作。这里我们将要介绍的 mysqldump 就属于 MySQL 提供的一个客户端应用程序。
该程序用于执行逻辑备份,产生一组能够被执行用于再现原始数据库对象定义和表数据的 SQL 语句。它与上述保存数据到文件中操作不同的是,它还能保存数据库及表的结构,即刚刚所述的原始数据库的对象定义。
mysqldump 作为一个客户端的应用程序,我们可以很方便的查看其使用方式和各个配置项的使用方式,使用如下命令:
$ mysqldump --help
下面我们列出官方所给出的三种常用于 mysqldump 的使用方法:
# 备份数据库或数据库中的一个或多个表
shell> mysqldump [options] db_name [tbl_name ...]
# 备份一个或多个数据库
shell> mysqldump [options] --databases db_name ...
# 备份所有的数据库
shell> mysqldump [options] --all-databases
例如,我们备份示例数据库 shiyanlou001,就可以使用如下语句:
$ mysqldump -u root --databases shiyanlou001 > dump.sql
需要注意的是,如果在上一节的账户管理操作部分修改了密码,则这里需要输入对应的 root 用户的密码
上述语句会产生一个 dump.sql 文件,该文件中包含能够重现 MySQL 数据库 shiyanlou003 的定义和数据的 sql 语句,因此,如果我们需要恢复该语句,只需要执行 dump.sql 文件中的 sql 语句即可。
为了演示这一操作,首先我们可以删除掉 shiyanlou001 数据库,然后通过 dump.sql 文件进行恢复:
# 删除 shiyanlou001 数据库
$ mysql -u root -p -e "DROP DATABASE IF EXISTS shiyanlou001;"
# 从 mysql.dump 文件中恢复 shiyanlou001 数据库
$ mysql -u root -p < dump.sql
# 恢复后,查看是否成功恢复
$ mysql -u root -p -e "USE shiyanlou001; SHOW TABLES;"
运行截图如下所示:
mysqldump 使用操作视频:
6. 二进制日志
对于上述两种方式的操作,其实我们只是在某一时刻对数据库进行了备份,即相当于该时刻数据库的一个快照,我们并不可能通过上面的方式随时备份数据。而当数据丢失或者损坏时,我们只能恢复已经备份的文件。
对于这个问题,我们可以通过日志来解决。二进制日志包含我们对数据库进行更改的事件,例如创建或者修改表等操作。我们可以通过其进行恢复操作。
6.1 启用日志
首先我们需要启用二进制日志,编辑 /etc/mysql/my.cnf 文件,在其中加入一行内容:
[mysqld]
...
log-bin = mysql_bin_log
...
编辑完成后,指定日志文件名为 mysql_bin_log,此时我们还需要重启 mysql,使用如下命令:
$ sudo service mysql restart
重启后,会在 /var/lib/mysql 路径下生成相应的日志文件,如下图所示:
图片中的 mysql_bin_log.000001 文件,会自动生成数字后缀,每次创建新的日志文件时,这个数字都会增加。而 mysql_bin_log.index 文件则是自动创建的二进制日志的索引文件,会记录相应的二进制日志文件。
6.2 mysqlbinlog
对于生成的日志来说,会以二进制的格式写入数据。所以如果需要查看二进制格式的日志,我们需要借助 mysqlbinlog 程序,该程序用于处理 MySQL 的二进制日志文件。
此时,我们可以尝试进行一些修改操作,例如我们创建一个 test_binlog 数据库:
mysql> CREATE DATABASE test_log;
Query OK, 1 row affected (0.00 sec)
创建成功之后,我们需要切换到 root 用户,并切换目录到 /var/lib/mysql 目录下,如下所示:
$ sudo su root
# cd /var/lib/mysql
这时,我们可以使用 mysqlbinlog 查看日志文件:
mysqlbinlog mysql_bin_log.000001
由于输出的信息较多,我们可以使用 grep 命令匹配信息。使用如下命令:
mysqlbinlog mysql_bin_log.000001 | grep "test_log"
可以看到记录的操作,此时我们手动删除掉 test_log 数据库,再通过日志恢复该数据库,相应的操作步骤如下:
- 手动删除 test_log 数据库:
mysql> DROP DATABASE test_binlog;
Query OK, 0 rows affected (0.02 sec)
- 通过 mysqlbinlog 得到一个包含 sql 语句的文件:
# root 用户下执行
mysqlbinlog mysql_bin_log.000001 > new_sql.txt
- 最后执行该文件中的 sql 语句即可:
mysql -u root -p < new_sql.txt
此时,刚刚删除的数据库就已经恢复成功了。
上述操作我们演示了一个非常简单的示例,但是 mysqlbinlog 还有很多其它常用的参数,例如下面两个参数:
- --start-datetime="2018-01-01 11:11:11" 指定开始时间
- --stop-datetime="2018-01-01 11:11:11" 指定结束时间
该选项可以用来截取指定时间段的日志,即通过这种方式,我们可以实现指定时间点的恢复等操作。
由于操作时间的不一致,这里我们就不演示用例,有兴趣的同学可以自行尝试
7. 总结
在本节实验内容中,我们介绍了一些用于备份和恢复的简单方法。