mariadb常用备份与还原工具介绍

本文涉及的产品
云数据库 RDS MySQL,集群系列 2核4GB
推荐场景:
搭建个人博客
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介:

一、备份的意义

从数据安全的角度来说,数据库服务器磁盘都会做RAID,Mariadb本身也有主从等容灾机制,但它们都无法完全取代备份。容灾和高可用能帮我们有效的应对物理的、硬件的、机械的故障,但是由我们手工操作导致的逻辑错误、系统本身内生性BUG、恶意攻击等方式破坏了你的数据却无能为力。每一种逻辑错误发生的概率都极低,但是当多种可能性叠加的时候,小概率事件就放大成很大的安全隐患,这时候备份的必要性就凸显了。

二、备份类型
1、根据备份时,数据库服务器是否在线:
  • 冷备:cold backup
  • 温备:warm backup
  • 热备:hot backup
2、根据备份的数据集:
  • 完全备份:full backup
  • 部分备份: partial backup
3、根据备份时的接口(直接备份数据文件还是通过mysql服务器导出数据):
  • 物理备份:直接复制(归档)数据文件的备份方式;physical backup
  • 逻辑备份:把数据从库中提出出来保存为文本文件;logical backup
4、根据备份时是备份整个数据还是仅备份变化的数据:
  • 完全备份:full backup
  • 增量备份:incremental backup
  • 差异备份:differential backup
5、备份策略:
  • 选择备份方式
  • 选择备份时间
  • 考虑到恢复成本
  • 恢复时长
  • 备份成本
  • 锁时间
  • 备份时长
  • 备份负载
6、备份对象
  • 数据
  • 配置文件
  • 代码:存储过程,存储函数,触发器
  • OS相关的配置文件,如crontab配置计划及相关的脚本,跟复制相关的配,二进制日志文件

三、常见的备份工具

mariadb本身为我们提供了mysqldump、mysqlbinlog备份工具,percona也为我们提供了强大的Xtrabackup,加上开源的mydumper,还有基于主从同步的延迟备份、从库冷备等方式,以及基于文件系统快照的备份,其实将这些方式合理搭配已经能够满足我们的需要了。而备份本身是为了恢复,所以能够让我们在出现故障后迅速、准确恢复的备份方式,就是最适合我们的,当然,同时能够省钱、省事,那就非常完美。下面就几种备份工具进行一些比较,探讨下它们各自的适用场景,及简单的使用做一下说明

1、mysqldump

⑴、mysqldump优缺点

mysqldump是最简单的逻辑备份方式(工作方式单线程)。在备份myisam表的时候,如果要得到一致的数据,就需要锁表,简单而粗暴。而在备份innodb表的时候,加上–master-data=2 –single-transaction 选项,在事务开始时刻,记录下binlog-pos点,然后利用mvcc(多版本并发控制)来获取一致的数据,由于是一个大事务,在写入和更新量很大的数据库上,将产生非常多的undo,显著影响性能,所以要慎用。

wKioL1NJY1PwpftQAAF4qal76Cc359.jpg

优点:简单,可针对单表备份,在完全导出表结构的时候尤其有用。可以做到对不同的存储引擎进行备份(InnoDB热备、MyISAM温备、Aria温备)

缺点:简单粗暴,单线程,备份慢而且恢复,不支持差异或增量备份,如果要进行差异或增量备份要结合binlog日志文件

mydumper是mysqldump的加强版。相比mysqldump:

内置支持压缩,可以节省2-4倍的存储空间。

支持并行备份和恢复,因此速度比mysqldump快很多,但是由于是逻辑备份,仍不是很快,如果要进行差异或增量备份要结合binlog日志文件

部分备份工具

SELECT clause INTO OUTFILE '/path/to/somefile'

LOAD DATA INFILE '/path/from/somefile'
不会备份关系定义,仅备份表中的数据;

逻辑备份工具,快于mysqldump。

⑵、mysqldump命令介绍及简单使用

①命令介绍

mysqldump [options] [db_name [tbl_name ...]]

备份单个库:

mysqldump [options] db_name

恢复时:如果目标库不存在,需要事先手动创建

options说明

--all-databases: 备份所有库
--databases db1 db2 ...: 备份指定的多个库
注意:备份前要加锁
--lock-all-tables:请求锁定所有表之后再备份,对MyISAM、InnoDB、Aria做温备
--single-transaction: 能够对InnoDB存储引擎实现热备;
备份代码:
--events: 备份事件调度器代码
--routines: 备份存储过程和存储函数
--triggers:备份触发器
备份时滚动日志:
--flush-logs: 备份前、请求到锁之后滚动日志;
复制时的同步位置标记:
--master-data=[0|1|2]
0: 不记录
1:记录为CHANGE MASTER语句
2:记录为注释的CHANGE MASTER语句

注意

使用mysqldump备份
请求锁:--lock-all-tables或使用--singe-transaction进行innodb热备;
滚动日志:--flush-logs
选定要备份的库:--databases
记录二进制日志文件及位置:--master-data=
恢复
建议:关闭二进制日志,关闭其它用户连接;
建议备份策略:基于mysqldump

备份部分

mysqldump+二进制日志文件;

恢复
完全备份+各二进制日志文件中至此刻的事件,对MySQL配置文件,以及与MySQL相关的OS配置文件在每次修改后都应该直接进行备份;

②、实例

要求如下:

备份所有数据库,要每周日凌晨自动执行;

③解决方案(此方法不唯一)

备份阶段

第一步,先远程登录到数据库上,事先看一下现有的数据库。

wKiom1NJaHbjzl5BAAH03wdWkVI320.jpg

由上图可见,除了hellodb数据库,其它的数据库都是系统自带的,看一下hellodb中有那些表,及当前binlog日志的记录的位置。

wKioL1NL446AziAXAAIAEWNmgcs930.jpg

由于当前数据库只有一个用户数据库,所以我们在乎其表的存储引擎来判断使用什么方式来备份。(是申请锁或是单事务)

wKioL1NJaebTRB0wAAN7xg3NH6I301.jpg

查看表状态发现hellodb中的表全部都是MyISAM的存储引擎,那么就可以使用申请全局锁来备份了。

1
# mysqldump -uroot -p  --all-databases --lock-all-tables --flush-logs --master-data=2 >/tmp/all.sql

wKiom1NJa7aiKc0oAAEZ8d6UEkE727.jpg

这时数据库全部备份完毕(此方法只适用于数据量不是很大,挑选一个相对并发的写请求不是特别多的时间或午夜备份。如果数据量特别大,此方法将不做参考范围)。

假如在这之后我们又在原来的库中建立新的表或插入数据,而在某一次我误操作删除了此数据库。将如何恢复?

wKiom1NL5PvhDZJ6AAEIzh4vRzk068.jpg

由上图可见,原来的hellodb数据库中多了一张表,为不演示恢复效果我将hellodb删除,看怎么样恢复

wKioL1NL5XuCCG1mAAE_TpkyEQM706.jpg

现在hellodb己经删除,看如何恢复。

恢复部分

第一步,将服务器离线,导出现在正在使用的binlog日志,还原之前的完全备份

查看完全备份中binlog日志的起始位置。

1
2
#vim /tmp/all.sql
-- CHANGE MASTER TO MASTER_LOG_FILE= 'mysql-bin.000002' , MASTER_LOG_POS=365;

将此日志中到删除数据库drop database hellodb之前的数据导出到存放起来,以供时间点还原之用

1
# mysqlbinlog --start-position=365 --stop-position=863  mysql-bin.000002 >/tmp/binlogbakup.sql

登录数据库

1
2
3
4
5
6
7
[root@localhost bin]# mysql
Welcome  to  the MariaDB monitor.  Commands  end  with  or  \g.
Your MariaDB  connection  id  is  4
Server version: 10.0.10-MariaDB-log Source distribution
Copyright (c) 2000, 2014, Oracle, SkySQL Ab  and  others.
Type  'help;'  or  '\h'  for  help. Type  '\c'  to  clear the  current  input statement.
MariaDB [(none)]>

注:此时要临时关闭二进制日志,关闭其它用户连接;

1
2
MariaDB [(none)]>  set  session sql_log_bin=0;
Query OK, 0  rows  affected (0.00 sec)

导入之前的备份 

1
MariaDB [(none)]> source /tmp/ all .sql

查看数据库中的表,里面没有我们备份之后建立的表,这时就用到了之前备份的binlog日志了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
MariaDB [test]> show databases;
+ --------------------+
Database            |
+ --------------------+
| hellodb            |
| information_schema |
| mysql              |
| performance_schema |
| test               |
+ --------------------+
rows  in  set  (0.05 sec)
MariaDB [test]> use hellodb
Database  changed
MariaDB [hellodb]> show tables;
+ -------------------+
| Tables_in_hellodb |
+ -------------------+
| classes           |
| coc               |
| courses           |
| scores            |
| students          |
| teachers          |
| toc               |
+ -------------------+
rows  in  set  (0.00 sec)

导入binlog日志,完全恢复数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
MariaDB [hellodb]> source /tmp/binlogbakup.sql
MariaDB [hellodb]> show tables;
+ -------------------+
| Tables_in_hellodb |
+ -------------------+
| addressbook       |
| classes           |
| coc               |
| courses           |
| scores            |
| students          |
| teachers          |
| toc               |
+ -------------------+
rows  in  set  (0.00 sec)
MariaDB [hellodb]>  select  from  addressbook;
+ --------+---------+----------+----------+-----------------+
| fname  | lname   | phone    | fax      | email           |
+ --------+---------+----------+----------+-----------------+
| France | D'Souza | 123 4567 | 000 7574 | fdz@ some .domain |
+ --------+---------+----------+----------+-----------------+
1 row  in  set  (0.01 sec)

这回数据就完整了。

附上一个基于mysqldump简单复制单库的myisam的脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#!/bin/sh
###############################
#此脚本用来单库完全份
#每个星期日做一次完全备份
###############################
#设置用户名和密码
v_user= "root"
v_password= "mysql"
#mysql安装可执行程序所在位置
MysqlDir= /usr/local/mari/bin
#备份数据库
database= "hellodb"
#设置备份路径,创建备份文件夹
BackupDir= /maria_bak
Full_Backup=$BackupDir /Full_backup
mkdir  -p $Full_Backup/$( date  +%Y%m%d)
#开始备份,记录备份开始时间
echo  '###' $( date  + "%Y-%m-%d %H:%M:%S" ) '###' "备份开始" >>$Full_Backup /full_buckup .log
$MysqlDir /mysqldump  -u$v_user -p$v_password --lock-all-tables --flush-logs --master-data=2 --databases $database>$Full_Backup/$( date  +%Y%m%d) /full_backup .sql
#压缩备份文件
gzip  $Full_Backup/$( date  +%Y%m%d) /full_backup .sql
echo  '###' $( date  + "%Y-%m-%d %H:%M:%S" ) '###' "备份完成" >>$Full_Backup /full_buckup .log

2. 基于文件系统的快照

基于文件系统的快照,是物理备份的一种。在备份前需要进行一些复杂的设置,在备份开始时刻获得快照并记录下binlog pos点,然后采用类似copy-on-write的方式,把快照进行转储。转储快照本身会消耗一定的IO资源,而且在写入压力较大的实例上,保存被更改数据块的前印象也会消耗IO,最终表现为整体性能的下降。而且服务器还要为copy-on-write快照预留较多的磁盘空间,这本身对资源也是一种浪费。因此这种备份方式在生产环境使用的不多。

wKiom1NL92TRAaKAAAEyVtHhHes360.jpg

注:

当刚为数数据文件所在的卷(源卷)创建快照时,此时的快照卷是空的。

当源卷中的数据发生变化后,则将发生变化的那部分数据复制到快照卷上,此时通过快照访问则是未发生变化的数据来自于源卷,变化后的数据来自于快照卷。

快照不是备份,它是仅是提供访问文件的一条通路,当源卷变化的量超出快照卷的大小时,快照卷将崩溃失效。

实例

前提:

首先,mariadb的数据文件在逻辑卷上,如果使用的是支持事务的存储引擎的话,那么要将事务日志与数据文件放在同一个卷上,这样才能保证数据的完整性。

看一下数据文件的属性

wKiom1NOgaDwFpRzAAJ8Ukod_iQ470.jpg

步骤:

备份部分

第一步,如果对源卷创建快照,那么首先要对数据库申请全局锁,滚动二进制日志,记录二进制日志位置标记

⑴、申请全局锁

1
2
3
4
5
6
7
8
9
10
11
12
MariaDB [(none)]> flush tables  with  read  lock;
Query OK, 0  rows  affected (0.00 sec)
MariaDB [(none)]> flush logs;
Query OK, 0  rows  affected (0.02 sec)
MariaDB [(none)]> show master status;
+ ------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+ ------------------+----------+--------------+------------------+
| mysql-bin.000012 |      365 |              |                  |
+ ------------------+----------+--------------+------------------+
1 row  in  set  (0.02 sec)
MariaDB [(none)]>

将此二进制日志信息记录下来

1
2
3
4
# mysql -e 'show master status;'>/maribak/binlog_pos/binlog_$(date +%F_%T).txt
# cat /maribak/binlog_pos/binlog_2014-04-14_04\:17\:13.txt
File    Position    Binlog_Do_DB    Binlog_Ignore_DB
mysql-bin.000012    365

此时可以创建快照了

1
2
# lvcreate -L 200M -s -n databak_snap -p r /dev/mydata/lv1
   Logical volume  "databak_snap"  created

第二步,创建完快照后解锁

当快照卷创建完成后,可以对数据库解锁(创建锁与解锁必须是同一会话,也就是说,当创建完锁之后,这个会话不可退出,之后还要在这个会话中对数据库解锁,一但退出,此会话的全局锁将失效)

1
2
MariaDB [(none)]> unlock tables;
Query OK, 0 rowsaffected (0.00 sec)

这时数据库又可以正常进行写入操作了


第三步,备份快照

挂载备份之后的快照

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@bogon mnt] # mount /dev/mydata/databak_snap  -o ro /mnt
[root@bogon mnt] # cd /mnt
[root@bogon mnt] # ll
total 176172
-rw-rw---- 1 mysql mysql    16384 Apr 14 04:11 aria_log.00000001
-rw-rw---- 1 mysql mysql       52 Apr 14 04:11 aria_log_control
-rw-r----- 1 mysql root      1099 Apr 14 04:11 bogon.err
-rw-rw---- 1 mysql mysql        5 Apr 14 04:11 bogon.pid
drwx------ 2 mysql mysql     4096 Apr 14 04:12 employees
-rw-rw---- 1 mysql mysql 79691776 Apr 14 04:14 ibdata1
-rw-rw---- 1 mysql mysql 50331648 Apr 14 04:14 ib_logfile0
-rw-rw---- 1 mysql mysql 50331648 Apr 14 04:14 ib_logfile1
-rw-rw---- 1 mysql mysql        0 Apr 14 04:11 multi-master.info
drwx------ 2 mysql root      4096 Apr 14 04:11 mysql
drwx------ 2 mysql mysql     4096 Apr 14 04:11 performance_schema
drwx------ 2 mysql root      4096 Apr 14 04:11  test

复制快照中的内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@bogon mnt] #  cp -a * /maribak/databak/
[root@bogon mnt] # cd /maribak/databak/
[root@bogon databak] # ll
total 176172
-rw-rw---- 1 mysql mysql    16384 Apr 14 04:11 aria_log.00000001
-rw-rw---- 1 mysql mysql       52 Apr 14 04:11 aria_log_control
-rw-r----- 1 mysql root      1099 Apr 14 04:11 bogon.err
-rw-rw---- 1 mysql mysql        5 Apr 14 04:11 bogon.pid
drwx------ 2 mysql mysql     4096 Apr 14 04:12 employees
-rw-rw---- 1 mysql mysql 79691776 Apr 14 04:14 ibdata1
-rw-rw---- 1 mysql mysql 50331648 Apr 14 04:14 ib_logfile0
-rw-rw---- 1 mysql mysql 50331648 Apr 14 04:14 ib_logfile1
-rw-rw---- 1 mysql mysql        0 Apr 14 04:11 multi-master.info
drwx------ 2 mysql root      4096 Apr 14 04:11 mysql
drwx------ 2 mysql mysql     4096 Apr 14 04:11 performance_schema
drwx------ 2 mysql root      4096 Apr 14 04:11  test

删除快照卷

1
#lvremove /dev/mydata/databak_snap

里源卷的数据库employees中添入一些数据。

1
2
3
MariaDB [employees]>  insert  employees  values (99, '1998-09-21' , 'JEYY' , 'jose' , 'M' , '2012-12-24' );
MariaDB [employees]>  insert  employees  values (999999, '1995-09-21' , 'king' , 'tom' , 'M' , '2012-12-24' );
MariaDB [employees]> insert  employees  values (5211345, '1995-09-21' , 'king' , 'tom' , 'F' , '2012-12-24' );

当备份过完成后,过了几个小时,数据库误删除了一些数据,这时就可用到此次完全备份与即时点还原日志。

1
2
3
MariaDB [employees]>  delete   from  employees   where  gender= 'M'
     -> ;
Query OK, 179975  rows  affected (3  min  52.82 sec)

还原部分

第一步,停止数据库服务,将备份后的数据中的某一张表或整个库替换此数据库中误删除的表或整个库。

1
2
[root@bogon employees] # service mari stop
Shutting down MySQL...............                         [  OK  ]

替换 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@bogon employees] # cp -a /maribak/databak/employees/employees.* .
cp : overwrite `. /employees .frm'? y
cp : overwrite `. /employees .ibd'? y
[root@bogon employees] # ll
total 237840
-rw-rw---- 1 mysql mysql        61 Apr 14 04:12 db.opt
-rw-rw---- 1 mysql mysql      1571 Apr 14 04:12 departments.frm
-rw-rw---- 1 mysql mysql    114688 Apr 14 04:12 departments.ibd
-rw-rw---- 1 mysql mysql      1999 Apr 14 04:12 dept_emp.frm
-rw-rw---- 1 mysql mysql  31457280 Apr 14 05:03 dept_emp.ibd
-rw-rw---- 1 mysql mysql      1999 Apr 14 04:12 dept_manager.frm
-rw-rw---- 1 mysql mysql    131072 Apr 14 04:55 dept_manager.ibd
-rw-rw---- 1 mysql mysql      1164 Apr 14 04:12 employees.frm
-rw-rw---- 1 mysql mysql  23068672 Apr 14 04:12 employees.ibd
-rw-rw---- 1 mysql mysql      1501 Apr 14 04:12 salaries.frm
-rw-rw---- 1 mysql mysql 146800640 Apr 14 05:03 salaries.ibd
-rw-rw---- 1 mysql mysql      1647 Apr 14 04:12 titles.frm
-rw-rw---- 1 mysql mysql  41943040 Apr 14 05:03 titles.ibd

注:此时文件的属性要与原文件的一致

第二步,启动服务

1
2
[root@bogon employees] # service mari start
Starting MySQL..                                           [  OK  ]

查看完全备份信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
MariaDB [employees]>  select  from  employees  where  hire_date > '2000-01-01' ;
+ --------+------------+------------+------------+--------+------------+
| emp_no | birth_date | first_name | last_name  | gender | hire_date  |
+ --------+------------+------------+------------+--------+------------+
|  47291 | 1960-09-09 | Ulf        | Flexer     | M      | 2000-01-12 |
|  60134 | 1964-04-21 | Seshu      | Rathonyi   | F      | 2000-01-02 |
|  72329 | 1953-02-09 | Randi      | Luit       | F      | 2000-01-02 |
| 205048 | 1960-09-12 | Ennio      | Alblas     | F      | 2000-01-06 |
| 222965 | 1959-08-07 | Volkmar    | Perko      | F      | 2000-01-13 |
| 226633 | 1958-06-10 | Xuejun     | Benzmuller | F      | 2000-01-04 |
| 227544 | 1954-11-17 | Shahab     | Demeyer    | M      | 2000-01-08 |
| 422990 | 1953-04-09 | Jaana      | Verspoor   | F      | 2000-01-11 |
| 424445 | 1953-04-27 | Jeong      | Boreale    | M      | 2000-01-03 |
| 428377 | 1957-05-09 | Yucai      | Gerlach    | M      | 2000-01-23 |
| 463807 | 1964-06-12 | Bikash     | Covnot     | M      | 2000-01-28 |
| 499553 | 1954-05-06 | Hideyuki   | Delgrande  | F      | 2000-01-22 |
+ --------+------------+------------+------------+--------+------------+

并没有备份后插入的数据,所以在用到binlog日志了,查看当前的binlog的位置

1
2
3
4
5
6
7
MariaDB [employees]> show master status;
+ ------------------+----------+--------------+------------------+
| File             | Position | Binlog_Do_DB | Binlog_Ignore_DB |
+ ------------------+----------+--------------+------------------+
| mysql-bin.000013 |      326 |              |                  |
+ ------------------+----------+--------------+------------------+
1 row  in  set  (0.00 sec)

跨文件了~~~,没关系统,先还原同一个日志文件中的数据,再恢复这个日志中的数据,在mysql-bin.000012中找到delect之前的位置,并使用mysqlbinlog将其导出,临时性关闭binlog日志功能,恢复此间数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
# mysqlbinlog /mysql/binlog/mysql-bin.000012
..........省略中........
# at 1108
#140414  4:53:25 server id 1  end_log_pos 1146  GTID 0-1-7138
/*!100001 SET @@session.gtid_seq_no=7138* // *!*/;
BEGIN
/*!*/;
# at 1146
#140414  4:53:25 server id 1  end_log_pos 1254  Query   thread_id=6 exec_time=233   error_code=0
SET TIMESTAMP=1397422405/*!*/;
delete  from employees  where gender= 'M'
/*!*/;
..........省略中........

从日志中看标识到1108即可恢复到删除之前,将此区间导出

1
2
3
[root@bogon binlog_pos] # mysqlbinlog --start-position=365 --stop-position=1108 /mysql/binlog/mysql-bin.000012 >zengliang.sql
[root@bogon binlog_pos] # ls
binlog_2014-04-14_04:17:13.txt  zengliang.sql

在数据中临时关闭binlog日志功能。

1
2
3
MariaDB [employees]>  set  session sql_log_bin=0;
Query OK, 0  rows  affected (0.01 sec)
MariaDB [employees]> source /maribak/binlog_pos/zengliang.sql;

查看一下原来的数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
MariaDB [employees]>  select  from  employees  where  hire_date > '2000-01-01' ;
+ ---------+------------+------------+------------+--------+------------+
| emp_no  | birth_date | first_name | last_name  | gender | hire_date  |
+ ---------+------------+------------+------------+--------+------------+
|      99 | 1998-09-21 | JEYY       | jose       | M      | 2012-12-24 |
|   47291 | 1960-09-09 | Ulf        | Flexer     | M      | 2000-01-12 |