对一个运行中的线上系统来说,定期对数据库进行备份是非常重要的,备份不仅可以确保数据的局部完整性,一定程度上也为数据安全性提供了保障,设想如果某种极端的场景下,比如磁盘损坏导致某个时间段数据丢失,或者误操作导致数据表数据被删等...
这种情况在现实中可以说无处不在,为了避免数据丢失或损坏带来的巨大损失,有必要对线上系统的数据定期做备份,而备份的直接好处就是,一旦数据需要做恢复的时候就可以利用这些备份数据快速恢复,从而最大程度减少损失。
根据实际的业务需求,经验参考可以据数据规模大小,服务器磁盘容量,大致可分为下面几种:
备份完整的数据库,全量数据就是数据库中所有的数据(或某一个库的全部数据);
增量数据就是指上一次全量备份数据之后到下一次全备之前数据库所更新的数据,对于mysqldump,binlog就是增量数据;
根据数据备份时对生产系统的影响,可以做如下分类:
停库,停服务,备份
这些备份操作在用户不能访问数据的时候进行,因此无法读取或修改数据。这些脱机备份会阻止执行任何使用数据的行为。这些类型的备份不会干扰正常运行的系统的性能。但是,对于某些应用程序,会无法接受必须在一段较长的时间里锁定或完全阻止用户访问数据。
不停库,不停服务,备份,也不会(锁表)阻止用户的写入
这些动态备份在读取或修改数据的过程中进行,很少中断或者不中断传输或处理数据的功能。使用热备份时,系统仍可供读取和修改数据的操作访问。
不停库,不停服务,备份,会(锁表) 阻止用户的写入
这些备份在读取数据时进行,但在多数情况下,在进行备份时不能修改数据本身。这种中途备份类型的优点是不必完全锁定最终用户。但其不足之处在于无法在进行备份时修改数据集,这可能使这种类型的备份不适用于某些应用程序。在备份过程中无法修改数据可能产生性能问题。
提前搭建好mysql服务(本文以mysql5.7为例进行说明),并创建一个测试使用的数据库,若干数据表;
mysqldump是mysql自带的数据备份命令,使用该命令可以完成数据库,数据表等多种备份策略,下面针对该命令的使用做详细的说明;
mysqldump [选项] 数据库名 [表名] > 脚本名
或
mysqldump [选项] --数据库名 [选项 表名] > 脚本名
或
mysqldump [选项] --all-databases [选项] > 脚本名
关于选项部分,包含的参数是比较多的,下面列举常用的一些参数选项
参数名 缩写 含义
--host -h 服务器IP地址
--port -P 服务器端口号
--user -u MySQL 用户名
--pasword -p MySQL 密码
--databases 指定要备份的数据库
--all-databases 备份mysql服务器上的所有数据库
--compact 压缩模式,产生更少的输出
--comments 添加注释信息
--complete-insert 输出完成的插入语句
--lock-tables 备份前,锁定所有数据库表
--no-create-db/--no-create-info 禁止生成创建数据库语句
--force 当出现错误时仍然继续备份操作
--default-character-set 指定默认字符集
--add-locks 备份数据库表时锁定数据库表
--no-create-db, ---取消创建数据库sql(默认存在)
--no-create-info,---取消创建表sql(默认存在)
--no-data ---不导出数据(默认导出)
--add-drop-database ---增加删除数据库sql(默认不存在)
--skip-add-drop-table ---取消每个数据表创建之前添加drop数据表语句(默认每个表之前存在drop语句)
--skip-add-locks ---取消在每个表导出之前增加LOCK TABLES(默认存在锁)
--skip-comments ---注释信息(默认存在)
mysqldump -uroot -pXXX --all-databases > /usr/local/mysql/full.sql
或者
mysqldump -uroot -pXXX -A > /usr/local/mysql/full.sql
mysqldump –u user –h host –p -- databases [ 数据库的名称 1 [ 数据库的名称 2...]] > 备份文件名称 .sql
mysqldump -uroot -pXXX --databases shake_date > /usr/local/mysql/shake_date_01.sql
或者
mysqldump -uroot -pXXX -B shake_date shake_flow > /usr/local/mysql/combine.sql
如果业务中不需要对全库做备份,只想备份部分表的时候,
mysqldump –u user –h host –p密码 数据库名 [ 表名 1 [ 表名 2...]] > 备份文件名称 .sql
备份shake_date 下面的t_user表
mysqldump -u用户名 -pXXX --no-data 数据库 数据表名称 > 备份sql文件名.sql
备份shake_date下面的t_user表,只备份表结构
mysqldump -uroot -pXXX --no-data shake_date t_user > /usr/local/mysql/t_user_bk2.sql
可以打开备份的文件检查下,可以发现这里就只剩下表结构;
如下,备份t_user表中age大于12岁的用户
mysqldump -uroot -pXXX shake_date t_user --where="age > 12 " > t_user_bk3.sql
如果备份某个库,但某些表数据量很大或与业务关联不大,这时候可以考虑排除这些表的备份,适用选项 --ignore-table ;
如下备份shake库下的表,排除t_no这个表
mysqldump -uroot -pXXX shake_date --ignore-table=shake_date.t_no > /usr/local/mysql/t_user_bk5.sql
在实际生产中,备份数据需要考虑的因素其实更多,考虑的越细致,在后续在数据恢复的时候就越精确,下面列举一些实际生产备份中可能用得上的额外的参数选项;
-R 备份存储过程及函数
--triggers 备份触发器
-E 备份事件
-F 在备份开始时,刷新一个新binlog日志
--master-data=2 以注释的形式,保存备份开始时间点的binlog的状态信息
--single-transaction innodb 存储引擎开启热备(快照备份)功能
--set-gtid-purged=auto
更多的参数,可以通过命令: mysqldump --help 进行查看和学习,每一项都有详细的介绍,可以结合官网一起学习;
在以上罗列的参数中,有下面三个参数选项这里做一下补充说明,也是备份过程中常常会涉及到的
可选值 : 1 ,2
通过mysqldump --help 命令可以清楚查看官方对该命令的解释,这里简单说下这个参数的作用
以--master-data=2为例进行说明
以注释的形式,保存备份开始时间点的binlog的状态信息
innodb 存储引擎开启热备(快照备份)功能
如果配合上面的master-data可以自动实现加锁
此为默认参数,即使在命令中不写,依旧生效,它的效果是在mysqldump输出的备份文件中生成SET@@GLOBAL.GTID_PURGED
语句;备份文件中的这条语句记录了GTID号, 可选值:auto , on
使用场景
off
时,在mysqldump
输出中不包含SET@@GLOBAL.GTID_PURGED
语句;适当调大该参数,可以避免在备份数据量过大时因数据落盘时数据包过大的备份失败问题
备份数据时连同触发器函数等也一同进行备份
操作演示
mysqldump -uroot -pXXX -A --master-data=2 --single-transaction --set-gtid-purged=OFF >/usr/local/mysql/shake_user_empty.sql
--set-gtid-purged=on
设置该参数之后,最明显的效果就是打开备份脚本文件,可以看到下面的信息,开启这个参数后在构建主从复制的时候会用上
--max-allowed-packet=#
在一些数据量比较大的备份情况下,建议适当的调大该参数
mysqldump -uroot -pXXX -B shake_date -R -E --triggers --master-data=2 --single-transaction --set-gtid-purged=OFF --max-allowed-packet=256M >/usr/local/mysql/shake_full_10.sql
使用mysqldump可以对备份的数据进行恢复,基本语法
mysql –u root –p [dbname] < backup .sql
如果备份文件中包含了创建数据库的语句,则恢复的时候不需要指定数据库名称
删除shake_date数据库
执行数据恢复,再次查看发现数据就回来了;
mysql -uroot -pXXX < /usr/local/mysql/full.sql
有整个实例的备份,但只想恢复某个数据库,这时可以从全量备份中分理出单个库的备份,可以参考下面的命令,比如将shake_date数据库从full.sql中分离出来;
sed -n '/^-- Current Database: `shake_date`/,/^-- Current Database: `/p' full.sql> /usr/local/mysql/shake_full.sql
这也是一种比较常见的需求了,比如业务中某些表的数据非常重要,优先恢复这些表,就可以用到这个功能,操作步骤如下:
cat shake_full.sql | sed -e '/./{H;$!d;}' -e 'x;/CREATE TABLE `t_user`/!d;q' > t_user_structure.sql
cat shake_full.sql | grep --ignore-case 'insert into `t_user`' > t_user_data.sql
首先,将shake_date库中的t_user表删除
依次执行上面的sql,即建表sql以及insert的数据sql从全库的sql备份中分离出来
可以打开一个看看
然后再执行数据的恢复
source /usr/local/mysql/t_user_structure.sql;
source /usr/local/mysql/t_user_data.sql;
下面说一个具体的场景,在生产环境下,假如定期对某个数据库进行全备,比如每周2的晚上进行全备,但现在发生了一件事,即在周三的时候,某位同事不小心将某张表的数据删除了,这该怎么办呢?
可以这么考虑,从周二全备那天来看,备份的数据是截止到那个时间点的全量数据,备份的时候可以将 master-data参数加进去,这样备份出来的数据中就有了那一刻binlog中的数据位置点,而在binlog日志中,记录了完整的操作记录行,那么就可以结合起来对数据进行快速恢复;
操作步骤如下:
给t1表随机插入几条数据
mysqldump -uroot -pXXX --databases mydb --master-data=2 --single-transaction --set-gtid-purged=ON >/usr/local/mysql/mydb.sql
注意:
--master-data=2 这个参数一定要开启,这个参数会记录备份那一刻的位置
恢复步骤
将备份的sql中的下面的信息定位到,CHANGE 开头的表示在binlog中记录的位置号为4001,这就是说,剩下的待恢复的数据从4001开始截取就可以了;
SET @@GLOBAL.GTID_PURGED='c38a5f35-67ac-11ed-aefa-525400633661:1-16';
CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000002', MASTER_LOG_POS=4001;
定位到mysql的binlog位置,结合上述的gtid,通过binlog进行恢复,使用下面的命令检查最后删除的位置点;
show binlog events in 'mysql-bin.000002';
在上面我们定位到备份中的sql文件中的gtid是1~16,这里最后drop的时候是20这个位置,则需要截取的那部分就是17 ~ 20;
mysqlbinlog --skip-gtids --include-gtids='c38a5f35-67ac-11ed-aefa-525400633661:17-20' mysql-bin.000002 > /usr/local/mysql/mydb_extend.sql
执行恢复
检查全备恢复后的数据,这样就恢复到了周一的数据;
接下来再恢复周2的数据;
source /usr/local/mysql/mydb_extend.sql;
所谓物理备份,最直接的就是将MySQL中的数据库文件复制出来,这种方式简单粗暴,速度也快,比较省事,
比如在上面创建了一个mydb的数据库,在mysql的数据目录下就存在下面几个数据文件;
但是这种方式并不是特别推荐,一方面在不同的操作系统下,数据目录有所不同,其次,这种备份的操作往往需要停服和锁表,以避免外部的操作给备份数据时带来的不一致的影响,另外,与mysql所使用的数据库引擎也很有关系;
在一些场景下,直接将数据库数据被分成sql文件可能并不是一个特别好的选择,因为导出来的文件格式不够通用,比如还有其他的数据分析平台需要使用这些数据的话,就比较麻烦了,这时候就可以考虑将数据库以文件的形式导出;
将shake_date库下的t_user表数据导出到文件
SELECT * FROM t_user INTO OUTFILE "/var/lib/mysql-files/tuser.txt";
打开该文件检查下
使用mysqldump命令将将shake_date数据库中tuser表中的记录导出到文本文件:
mysqldump -uroot -pXXX -T "/var/lib/mysql-files/" shake_date t_user
mysqldump -uroot -pXXX -T "/var/lib/mysql-files/" shake_date t_user --fields-terminated-by=',' --fields-optionally-enclosed-by='\"'
可以检查下txt文件 ,这种格式的数据在很多大数据处理业务场景中是一种比较好的数据格式
mysql -uroot -pXXX --execute="SELECT * FROM t_user;" shake_date> "/var/lib/mysql-files/tuser1.txt"
mysql -uroot -pXXX --vertical --execute="SELECT * FROM t_user;" shake_date > "/var/lib/mysql-files/tuser2.txt"
得到的数据格式如下
使用 --xml参数将数据导出为xml格式
mysql -uroot -pXXX --xml --execute="SELECT * FROM t_user;" shake_date>"/var/lib/mysql-files/tuser3.xml"
上面演示了将表以各自类型格式的文件导入,下面再看看如何将不同格式的表的数据进行导入操作;
使用 SELECT...INTO OUTFILE 将 shake_date 数据库中 t_user 表的记录导出到文本文件
执行下面的语句导出
SELECT * FROM shake_date.t_user INTO OUTFILE '/var/lib/mysql-files/tuser1.txt';
删除t_user表的数据
从文本文件tuser1.txt中恢复数据
LOAD DATA INFILE '/var/lib/mysql-files/tuser1.txt' INTO TABLE shake_date.t_user;
使用如下语句
LOAD DATA INFILE '/var/lib/mysql-files/t_user.txt' INTO TABLE shake_date.t_user FIELDS TERMINATED BY ',' ENCLOSED BY '\"';
导入之前先清理下表的数据
仍然以上面的那个以逗号分割的文件为例进行说明
先清理数据表
执行下面的语句进行数据导入
mysqlimport -uroot -pXXX shake_date '/var/lib/mysql-files/t_user.txt' --fields-terminated-by=',' --fields-optionally-enclosed-by='\"'
执行完成后,可以看到数据又恢复了;