• 微信公众号:美女很有趣。 工作之余,放松一下,关注即送10G+美女照片!

12 数据备份

开发技术 开发技术 3小时前 1次浏览

备份介绍

为何要备份数据

在生产环境中,我们数据库可能会遭遇各种各样的不测,从而导致数据丢失,大致可以分为以下几种

硬件故障
软件故障
自然灾害
黑客攻击
误操作(占比最大)

我们要备份什么

数据
二进制日志,innodb事务日志
代码(存储过程、存储函数、触发器、事件调度器)
服务器配置文件

备份的类型

  • 冷备

    数据库进行备份时,数据库不能进行读写操作,即数据库要下线

  • 温备

    不停库、不停服来进行备份,会锁表,阻止用户的写入,即当数据库进行备份时,数据库的读操作可以正常执行,但是不可以执行写操作

  • 热备(建议):不停库、不停服来进行备份,也不会锁表阻止用户的写入,即当数据库进行备份的时候,数据库的读写操作均不是受影响

备份的工具

12  数据备份

设计备份策略

备份数据的策略要根据不同的应用场景进行定制, 大致有几个参考数值, 我们可以根据这些数值从而定制符合特定环境中的数据备份策略。

能够容忍丢失多少数据
恢复数据需要多长时间
需要恢复哪一些数据

三种备份策略及应用场景

针对不同的场景下, 我们应该制定不同的备份策略对数据库进行备份, 一般情况下, 备份策略一般为以下三种:

直接cp,tar复制数据库文件(基本上可以忽略这一种了,“手动狗头”)
mysqldump+复制bin logs
lvm2快照+复制bin logs
xtrabackup
以上的几种解决方案分别针对于不同的场景

如果数据量较小, 可以使用第一种方式, 直接复制数据库文件
如果数据量还行, 可以使用第二种方式, 先使用 mysqldump对数据库进行完全备份, 然后定期备份binary log达到增量备份的效果
如果数据量一般, 而又不过分影响业务运行, 可以使用第三种方式, 使用lvm2的快照对数据文件进行备份, 而后定期备份binary log达到增量备份的效果
如果数据量很大, 而又不过分影响业务运行, 可以使用第 四种方式, 使用xtrabackup进行完全备份后, 定期使用xtrabackup进行增量备份或差异备份

备份实战

使用mysqldump + 复制binary logs备份

mysqldump命令使用

语法:
mysqldump -h 服务器 -u 用户名 -p密码 选项与参数 > 备份文件.sql

选项与参数:
-A/--all-databases 					所有库
-B/--databases bbs db1 db2 			多个数据库
db1 								数据库名
db1 t1 t2 							db1数据库的表t1、t2
-F 									备份的同时刷新bin log
-R 									备份存储过程和函数数据(如果开发写了函数和存储过程,就备,没写就不备)
--triggers 							备份触发器数据(现在都是开发写触发器)
-E/--events 						备份事件调度器
-d 									仅表结构
-t 									仅数据

--master-data=1 				
备份文件中 change master语句是没有注释的,默认为1
用于已经制作好了主从,现在想扩展一个从库的时候使用。
如此备份,扩展添加从库时导入备份文件后便不需要再加mater_pos了。
change matser to
master_host='10.0.0.111'
master_user='rep'
master_password=123
master_log_pos=120
master_log_file='master-bin.000001'

--master-data=2 
备份文件中change master语句是被注释的

--lock-all-tables, 提交请求锁定所有数据库中的所有表,以保证数据的一致性。
这是一个全局读锁,并且自动关闭--single-transaction和--lock-tables选项,他们是互斥的。
对于支持事务的表,例如InnoDB和BDB,推荐使用--single-transaction选项,因为它根本不需要锁定表。

--single-transaction: 快照备份 (搭配--master-data可以做到热备)
该选项在导出数据之前提交一个BEGIN SQL语句,BEGIN不会阻塞任何应用程序且能保证导出时数据库的一致性状态。它只适用于多版本存储引擎,仅InnoDB。
本选项和--lock-tables选项是互斥的,不能同时存在,因为LOCK TABLES会使任何挂起的事务隐式提交。

# 完整语句
mysqldump -uroot -p123 -A -R --triggers --master-data=2 --single-transaction > /tmp/full.sql

# 文件太大时可以压缩 gzip ,但是gzip不属于mysql独有的命令,可以利用管道
# --master-data=2 --single-transaction 这两个选项一起使用等于热备
mysqldump -uroot -p123 -A -R --triggers --master-data=2 --single-transaction | gzip >/tmp/full$(date +%F).sql.gz

# 导出时压缩了,导入时需要解压,可以使用zcat命令,很方便
zcat /tmp/full$(date +%F).sql.gz | mysql -uroot -p123

案例

1. 先打开binlog日志
[root@db01 ~]# vim /etc/my.cnf
[mysqld]
server_id=1
log-bin=/service/mysql/mybinlog
binlog_format='row'		 	# (row,statement,mixed)
binlog_rows_query_log_events=on
max_binlog_size=100M

# 重启数据库
[root@db01 ~]# systemctl restart mysql

2. 登录数据库,插入测试数据
mysql> create database db01;
Query OK, 1 row affected (0.00 sec)

mysql> use db01;
Database changed

mysql> create table t1(id int);
Query OK, 0 rows affected (0.00 sec)

mysql> insert t1 values(1),(2),(3);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

3. 在命令行执行命令,进行全量备份
[root@db01 ~]# mysqldump -uroot -p123 -A -R --triggers --master-data=2 --single-transaction | gzip > /tmp/full.sql.gz
Warning: Using a password on the command line interface can be insecure.

4. 在命令行执行命令,刷新bin log,便于日后查找(或者直接在上一条sql语句中加上-F参数也可以)
[root@db01 ~]# mysql -uroot -p123 -e "flush logs"

5. 登录数据库,再插入一些数据,模拟增量,这些数据写入了新的binlog
mysql> use db01;
Database changed

mysql> insert t1 values(4),(5),(6);
Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

模拟数据丢失
mysql> drop database db01;
Query OK, 1 row affected (0.01 sec)

# 查看事件详细信息,我们在做全量备份的时候刷新了bin log日志,所以我们查看的日志文件为mybinlog.000002
mysql> show binlog events in 'mybinlog.000002';
[root@db01 ~]# mysqlbinlog /service/mysql/mybinlog.000002

导出增量备份日志的数据
# 注意:导出bin log时不要加选项--base64-output
[root@db01 ~]# mysqlbinlog /service/mysql/mybinlog.000002 --start-position=192 --stop-position=338 > /tmp/1.sql


恢复数据
1. mysql数据导入时,临时关闭bin log,不要将恢复数据的写操作也记入
mysql> set sql_log_bin=0;
Query OK, 0 rows affected (0.00 sec)

2. 先恢复全量
# 如果不是压缩包直接使用:source /tmp/full.sql
mysql> system zcat /tmp/full.sql.gz | mysql -uroot -p123
Warning: Using a password on the command line interface can be insecure.

3. 再恢复增量
mysql> source /tmp/1.sql;

4. 开启二进制日志,恢复数据的写操作也记入bin log文件
mysql> set sql_log_bin=1;
Query OK, 0 rows affected (0.00 sec)

LVM备份

1. 添加硬盘; 这里我们直接实现SCSI硬盘的热插拔, 首先在虚拟机中添加一块硬盘, 无需重启
echo '- - -' > /sys/class/scsi_host/host0/scan
echo '- - -' > /sys/class/scsi_host/host1/scan
echo '- - -' > /sys/class/scsi_host/host2/scan

2. 创建逻辑卷
pvcreate /dev/sdb
vgcreate vg1 /dev/sdb
lvcreate -n lv1 -L 5G vg1

3. 格式化制作文件系统并挂载到指定目录下
mkfs.xfs /dev/mapper/vg1-lv1

# 将/var/lib/mysql的文件都暂时移动到/opt目录下
mv /var/lib/mysql/* /opt/

# 挂载
[root@db01 ~]# mount /dev/mapper/vg1-lv1 /var/lib/mysql
# 移动回去

4. 先打开binlog日志
[root@db01 ~]# vim /etc/my.cnf
[mysqld]
server_id=1
log-bin=/service/mysql/mybinlog
binlog_format='row'		 	# (row,statement,mixed)
binlog_rows_query_log_events=on
max_binlog_size=100M

# 重启数据库
[root@db01 ~]# systemctl restart mysql

5. 往数据库内插入测试数据
create database db01;
use db01;
create table t1(id int);
insert t1 values(1),(2),(3);

全备

mysql> FLUSH TABLES WITH READ LOCK; 		 # 锁定所有表
Query OK, 0 rows affected (0.00 sec)

[root@node1 lvm_data]# lvcreate -L 1G -s -n lv1_from_vg1_snap /dev/vg1/lv1  # 创建快照卷

# 刷新bin log日志,便于日后查找
[root@db01 ~]# mysql -uroot -p123 -e "flush logs"

mysql> UNLOCK TABLES; 			# 解锁所有表
Query OK, 0 rows affected (0.00 sec)

[root@node1 lvm_data]# mkdir /snap1 		# 创建文件夹
[root@node1 lvm_data]# mount -o nouuid /dev/vg1/lv1_from_vg1_snap /snap1

[root@localhost snap1]# cd /snap1/
[root@localhost snap1]# tar cf /tmp/mysqlback.tar *

[root@localhost snap1]# umount -l /snap1/ 
[root@localhost snap1]# lvremove vg1/lv1_from_vg1_snap

增备

模拟数据丢失
use db01;
create table t2(id int);
insert t2 values(1),(2),(3);
drop database db01;

# 查看事件详细信息,我们在做全量备份的时候刷新了bin log日志,所以我们查看的日志文件为mybinlog.000002
mysql> show binlog events in 'mybinlog.000002';
[root@db01 ~]# mysqlbinlog /service/mysql/mybinlog.000002

导出增量备份日志的数据
# 注意:导出bin log时不要加选项--base64-output
[root@db01 ~]# mysqlbinlog /service/mysql/mybinlog.000002 --start-position=120 --stop-position=468 > /tmp/1.sql

1. 先恢复全量
[root@db01 ~]# tar -xf /tmp/mysqlback.tar -C /service/

2. 再恢复增量
#  mysql数据导入时,临时关闭bin log,不要将恢复数据的写操作也记入
mysql> set sql_log_bin=0;

mysql> source /tmp/1.sql;

#  开启二进制日志,恢复数据的写操作也记入bin log文件
mysql> set sql_log_bin=1;

Xtrabackup备份

Xtrabackup是由percona提供的mysql数据库备份工具,据官方介绍,这也是世界上惟一一款开源的能够对innodb和xtradb数据库进行热备的工具。特点:

  1. 备份过程快速、可靠;

  2. 备份过程不会打断正在执行的事务;

  3. 能够基于压缩等功能节约磁盘空间和流量;

  4. 自动实现备份检验;

  5. 还原速度快;

使用xtrabackup使用InnoDB能够发挥其最大功效, 并且InnoDB的每一张表必须使用单独的表空间, 我们需要在配置文件中添加 innodb_file_per_table = ON 来开启。

安装脚本,当然你也可以一个个执行

#!/usr/bin/env bash
cd /opt
wget https://repo.percona.com/yum/percona-release-latest.noarch.rpm
yum install percona-release-latest.noarch.rpm -y
yum install percona-xtrabackup-24 -y

全备

# 创建备份目录
[root@db01 ~]# mkdir /backup

# 全备
[root@db01 ~]# innobackupex --user=root --password=xxx! /backup/full

# 查看
[root@db01 ~]# cd /backup/
[root@db01 backup]# ls
full
[root@db01 backup]# cd full/
[root@db01 full]# ls
2021-07-21_20-31-12

# 去掉时间戳
[root@db01 full]# innobackupex --user=root --password=xxx! --no-timestamp /backup/full
[root@db01 full]# ls
backup-my.cnf   mysql                   xtrabackup_checkpoints
egon_test       performance_schema      xtrabackup_info
ib_buffer_pool  sys                     xtrabackup_logfile
ibdata1         xtrabackup_binlog_info

# 补充:关于备份目录下新增的文件说明,可用cat命令查看 xtrabackup_checkpoints 存储系统版本号,增备的时候会 用到xtrabackup_info 存储UUID,数据库是由自己的UUID的, 如果相同,做主从会有问题 xtrabackup_logfile 就是redo

增备

--incremental:开启增备功能
--incremental-basedir:上一次备份的路径

# 增备
innobackupex --user=root --password=xxx! --no-timestamp --incremental --incremental-basedir=/backup/full /backup/xtra

# 查看
[root@db01 ~]# cd /backup/
[root@db01 backup]# ls
full  xtra

# 查看是否衔接
[root@db01 backup]# cat full/xtrabackup_checkpoints 
backup_type = full-backuped
from_lsn = 0
to_lsn = 5908495
last_lsn = 5908504
compact = 0
recover_binlog_info = 0
flushed_lsn = 5908504
[root@db01 backup]# cat xtra/xtrabackup_checkpoints 
backup_type = incremental
from_lsn = 5908495
to_lsn = 5908495
last_lsn = 5908504
compact = 0
recover_binlog_info = 0
flushed_lsn = 5908504

案例–备份

# 开启binlog
vim /etc/my.cnf 
[mysqld] 
server_id=1 				 -- 服务ID,主从库必须不一样,必须指定
log-bin=/service/mysql/mybinlog 		-- 此变量用于控制是否开启二进制日志,而且这是一个只读变量,默认值为OFF
binlog_format='row'			-- (row,statement,mixed),不建议随意去修改binlog工作模式 
binlog_rows_query_log_events=on 		 -- 打开才能查看详细记录,默认为off
max_binlog_size=100M			-- 设置单个二进制日志文件的最大大小,超出100M。MySQL默认会新建一个文件记录日志

# 你权限有问题自己解决

# 重启mysql
systemctl restart mysqld

# 插数据
create database egon_test;
use egon_test;
create table employee(id int not null unique auto_increment,name varchar(20) not null,sex enum('male','female') not null default 'male', age int(3) unsigned not null default 28,hire_date date not null,post varchar(50),post_comment varchar(100),salary double(15,2),office int, depart_id int);#插入记录#三个部门:教学,销售,运营

insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values('egon','male',18,'20170301','God',7300.33,401,1),('alex','male',78,'20150302','teacher',1000000.31,401,1),('wupeiqi','male',81,'20130305','teacher',8300,401,1),('yuanhao','male',73,'20140701','teacher',3500,401,1),('liwenzhou','male',28,'20121101','teacher',2100,401,1),('jingliyang','female',18,'20110211','teacher',9000,401,1),('jinxin','male',18,'19000301','teacher',30000,401,1);

# 第一次全备
innobackupex --user=root --password=xxx! --no-timestamp /backup/full

# 周一继续插数据
use egon_test;
insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values('egon','male',18,'20170301','God',7300.33,401,1),('alex','male',78,'20150302','teacher',1000000.31,401,1),('wupeiqi','male',81,'20130305','teacher',8300,401,1),('yuanhao','male',73,'20140701','teacher',3500,401,1),('liwenzhou','male',28,'20121101','teacher',2100,401,1),('jingliyang','female',18,'20110211','teacher',9000,401,1),('jinxin','male',18,'19000301','teacher',30000,401,1);

# 周一增备
innobackupex --user=root --password=xxx! --no-timestamp --incremental --incremental-basedir=/backup/full /backup/xtra

# 周二继续插数据
use egon_test;
insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values('egon','male',18,'20170301','God',7300.33,401,1),('alex','male',78,'20150302','teacher',1000000.31,401,1),('wupeiqi','male',81,'20130305','teacher',8300,401,1),('yuanhao','male',73,'20140701','teacher',3500,401,1),('liwenzhou','male',28,'20121101','teacher',2100,401,1),('jingliyang','female',18,'20110211','teacher',9000,401,1),('jinxin','male',18,'19000301','teacher',30000,401,1);

# 周二增备
innobackupex --user=root --password=xxx! --no-timestamp --incremental --incremental-basedir=/backup/xtra /backup/xtra1

# 周三插入数据
use egon_test;
insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values('egon','male',18,'20170301','God',7300.33,401,1),('alex','male',78,'20150302','teacher',1000000.31,401,1),('wupeiqi','male',81,'20130305','teacher',8300,401,1),('yuanhao','male',73,'20140701','teacher',3500,401,1),('liwenzhou','male',28,'20121101','teacher',2100,401,1),('jingliyang','female',18,'20110211','teacher',9000,401,1),('jinxin','male',18,'19000301','teacher',30000,401,1);

# 然后数据库没了
# bomb 纱卡拉卡
[root@db01 mysql]# rm -rf /var/lib/mysql

# 恢复思路
1. 停业务,挂维护页 2. 查找可用备份并处理备份:full+inc1+inc2 3. 找到binlog中: inc2 到 故障时间点的binlog 4. 恢复全备+增量+binlog 5. 验证数据 6. 起业务,撤维护页

案例–恢复

# 整理full
[root@db01 backup]# innobackupex --apply-log --use-memory=3G --redo-only /backup/full

# 合并周一的增量到full并整理
[root@db01 backup]# innobackupex --apply-log --use-memory=3G --redo-only --incremental-dir=/backup/xtra /backup/full

# 合并周二的增量到full并整理
[root@db01 backup]# innobackupex --apply-log --use-memory=3G --redo-only --incremental-dir=/backup/xtra1 /backup/full

# 整理full
[root@db01 backup]# innobackupex --apply-log --use-memory=3G /backup/full

# 查看周三的binlog_info
[root@db01 backup]# cat xtra1/xtrabackup_binlog_info 
mybinlog.000001	4350

[root@db01 backup]# mysqlbinlog /data/binlog/mybinlog.000001 -- start-position=4350 > /backup/binlog.sql # 起始点选择上面的

# 授权
chown -R mysql.mysql /var/lib/mysql

systemctl start mysqld
source /backup/binlog.sql

数据库的快速导入和导出

在公司中,如果运营或者产品手里有几千万甚至几亿条数据,要求你将其导入数据中,请问如何做?
如果你依据运营或产品交给你的数据文件直接使用insert语句,一行一行地批量插入,那至少需要1-2天时间才能插入完毕,此时我们可以用load data infile语句。 load data infile语句可以从一个文本文件中,将数据以很高的速度读入一个表中。MySQL官方文档也说明了,该方法比一次性插入一条数据性能快20倍。此外,mysql也支持快速导出语句select into outfile 。
使用MySQL的select into outfile、load data infile快速导出导入数据,12G的数据导出用时3分钟左右,导入用时4分钟左右(执行时间根据机器的配置会有所不同,不具有参考价值)。

快速导出

语法:
select ... into outfile 导出文本文件

要想导出成功,需要设置安全目录才行
vim /etc/my.cnf
[mysqld]
secure-file-priv=/tmp			# 指定导出的目录
SELECT * FROM db1.t1
INTO OUTFILE '/tmp/db1_t1.txt'
FIELDS TERMINATED BY ',' 			-- 定义字段分隔符
OPTIONALLY ENCLOSED BY '"' 			-- 定义字符串使用什么符号括起来
LINES TERMINATED BY 'n'; 			-- 定义换行符

快速导入

mysql> delete from t1; 
mysql> create table new_t1(表结构与文件中数据保持一致); 
mysql> LOAD DATA INFILE '/tmp/db1_t1.txt'
  	   INTO TABLE new_db.new_t1
  	   FIELDS TERMINATED BY ','
  	   OPTIONALLY ENCLOSED BY '"'
  	   LINES TERMINATED BY 'n';

花里胡哨导出

mysql -u root -p123 -e 'select * from db1.t1' > /tmp/db1_t1.txt 
mysql -u root -p123 --xml -e 'select * from db1.t1' > /tmp/db1_t1.xml 		-- xml文件跨平台性
mysql -u root -p123 --html -e 'select * from db1.t1' > /tmp/db1_t1.html

案例

# 建表
create database db01;
use db01;

create table employee(
id int not null unique auto_increment,
name varchar(20) not null,
sex enum("male","female") not null default "male",  
age int(3) unsigned not null default 28,
hire_date date not null,
post varchar(50),
post_comment varchar(100),
salary double(15,2),    
office int, 
depart_id int
);

# 插入数据
insert into employee(name,sex,age,hire_date,post,salary,office,depart_id) values
("sun","male",18,"20170301","猥琐欲为",1000.22,401,1),   
("haha","male",78,"20150302","teacher",10000.31,401,1),
("xixi","male",81,"20130305","teacher",8300.31,401,1),
("dudu","male",72,"20150312","teacher",3100,401,1);

# 快速导出数据
# 修改配置文件,指定导出的目录
vim /etc/my.cnf
[mysqld]
secure-file-priv=/tmp

# 重启数据库
systemctl restart mysql

# 导出数据
mysql> SELECT * FROM db01.employee
    -> INTO OUTFILE '/tmp/employee.txt'
    -> FIELDS TERMINATED BY ','
    -> OPTIONALLY ENCLOSED BY '"'
    -> LINES TERMINATED BY 'n';
    -> LINES TERMINATED BY 'n';
Query OK, 4 rows affected (0.00 sec)

# 查看导出的数据
cat /tmp/employee.txt 
# 1,"sun","male",18,"2017-03-01","猥琐欲为",N,1000.22,401,1
# 2,"haha","male",78,"2015-03-02","teacher",N,10000.31,401,1
# 3,"xixi","male",81,"2013-03-05","teacher",N,8300.31,401,1
# 4,"dudu","male",72,"2015-03-12","teacher",N,3100.00,401,1

# 快速导入数据
#  创建表,表结构与数据保持一致
create table test(
id int not null unique auto_increment,
name varchar(20) not null,
sex enum("male","female") not null default "male",  
age int(3) unsigned not null default 28,
hire_date date not null,
post varchar(50),
post_comment varchar(100),
salary double(15,2),    
office int, 
depart_id int
);

# 导人数据
mysql> LOAD DATA INFILE '/tmp/employee.txt'
    -> INTO TABLE db01.test
    -> FIELDS TERMINATED BY ','
    -> OPTIONALLY ENCLOSED BY '"'
    -> LINES TERMINATED BY 'n';
Query OK, 4 rows affected (0.00 sec)
Records: 4  Deleted: 0  Skipped: 0  Warnings: 0

# 查看导入的新表数据
mysql> select * from db01.test;
+----+------+------+-----+------------+--------------+--------------+----------+--------+-----------+
| id | name | sex  | age | hire_date  | post         | post_comment | salary   | office | depart_id |
+----+------+------+-----+------------+--------------+--------------+----------+--------+-----------+
|  1 | sun  | male |  18 | 2017-03-01 | 猥琐欲为     | NULL         |  1000.22 |    401 |         1 |
|  2 | haha | male |  78 | 2015-03-02 | teacher      | NULL         | 10000.31 |    401 |         1 |
|  3 | xixi | male |  81 | 2013-03-05 | teacher      | NULL         |  8300.31 |    401 |         1 |
|  4 | dudu | male |  72 | 2015-03-12 | teacher      | NULL         |  3100.00 |    401 |         1 |
+----+------+------+-----+------------+--------------+--------------+----------+--------+-----------+
4 rows in set (0.00 sec)

程序员灯塔
转载请注明原文链接:12 数据备份
喜欢 (0)