tlsmile 2020-01-28
利用Linux的LVM技术来实现热备份,将MySQL的数据目录放到LVM逻辑卷上,然后通过LVM快照技术备份逻辑卷的内容。第一次备份是全量备份,之后的备份都是增量备份。在还原时,将快照中的数据目录恢复到ySQL的数据目录即可。
使用LVM这种技术不仅可以备份MySQL还可以备份MongoDB等其他数据库,但使用LVM做热备份方案也比较麻烦,因为需要手动创建逻辑卷、迁移数据目录、创建快照以及给数据库加锁等等,所以LVM并不是常用的热备份方案。
因为LVM的麻烦,所以人们都希望使用专业的工具去做热备份,这个工具就是XtraBackup。XtraBackup是由Percona开源的免费数据库热备份工具,它能对InnoDB数据库和XtraDB存储引擎的数据库非阻塞地备份。因为XtraBackup在备份过程中不会打断正在执行的事务,而事务日志中记录了哪些是备份前写入的数据哪些是备份后写入的数据,所以无需加锁。
另外,XtraBackup提供了对备份数据的压缩功能,可以节约备份文件占用的磁盘空间及网络带宽。但XtraBackup在备份使用MyISAM作为存储引擎的表时会加读锁,即表中的数据可读但不可写,不过这也不是问题,之前提到了可以使用联机热备份的方式来解决加读锁的问题。同样,XtraBackup支持全量备份和增量备份,因为XtraBackup的方便性,所以一般都是采用XtraBackup来做热备份方案。
因为XtraBackup是主流的MySQL热备份方案,所以这里简单介绍一下XtraBackup热备份原理:
现在已经知道了XtraBackup是通过读取并复制底层的数据文件,完成物理备份的。其中全量备份比较简单,直接备份数据文件中的所有内容即可。而增量备份则需要区分新数据和旧数据,然后仅备份新数据,所以稍微复杂以些。
XtraBackup增量备份的原理如下:
增量备份只备份新增的数据,所以XtraBackup会去读取数据文件中的内容来判断哪些是旧数据哪些是新数据,然后只去备份新数据。在MySQL的数据文件里,数据是存放在
row
这种结构中的,而row
存放在page
中,page
则存在于extend
中。MySQL会为每一个
page
都标记上一个LSN
编号,通过对比该编号就可以得知哪些数据是新的,哪些数据是旧的。然后XtraBackup只需要从数据文件中,将这些新的page
数据备份出来就行了。示意图:
那么XtraBackup是如何得知哪些LSN
是新的呢?首先我们要知道LSN
是一个全局递增的编号,每次对page
中的数据进行修改时都会产生新的LSN
编号。假设现在有6个page
,各自的LSN
编号如下:
上图中,LSN
编号为3的表示被修改过3次,LSN
编号为5的表示被修改过5次,以此类推。假设此时对这些page
进行一次全量备份,那么这6个page
都会被备份下来。经过一段时间后,其中有三个page
被修改了,LSN
编号发生了变化,如下所示:
当进行增量备份时,XtraBackup就会将之前备份的page
的LSN
编号与数据文件中与之对应的page
的LSN
编号进行对比,若数据文件里的LSN
编号大于备份里的LSN
编号则代表数据文件中的page
是新数据,那么XtraBackup就会对其进行备份。若等于则代表数据没有发生变化,不进行备份。因为LSN
编号是全局递增的,所以不存在小于的情况。这就是XtraBackup增量备份的原理。
在了解了XtraBackup和其热备份的实现原理后,接下来我们实践一下如何使用该工具。首先需要安装官方提供的yum
仓库:
[ ~]# yum install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm
然后激活该yum
仓库:
[ ~]# percona-release setup ps80
现在就可以直接通过yum
命令本地安装XtraBackup了:
[ ~]# yum install -y percona-xtrabackup-24
安装完成后,输出一下版本信息验证xtrabackup
命令是否可用:
[ ~]# xtrabackup --version xtrabackup: recognized server arguments: --server-id=3 --datadir=/var/lib/mysql --log_bin xtrabackup version 2.4.18 based on MySQL server 5.7.26 Linux (x86_64) (revision id: 29b4ca5) [ ~]#
XtraBackup命令种类:
序号 | 命令 | 描述 |
---|---|---|
1 | xbcrypt | 用于加密或解密备份的数据 |
2 | xbstream | 用于压缩或者解压缩xbstream文件 |
3 | xtrabackup | 用于备份使用InnoDB、XtraDB作为存储引擎的数据表,该命令仅备份表数据文件,所以通常用在增量备份上 |
4 | innobackupex | 是上面三种命令的perl脚本封装,可以使用该命令备份MyISAM数据表,并且只有使用该命令才能备份表数据文件、表定义文件、表索引文件,所以通常用在全量备份上 |
使用innobackupex
命令备份InnoDB数据表的流程图:
使用innobackupex
命令进行全量热备份示例:
[ ~]# innobackupex --defaults-file=/etc/my.cnf --host=192.168.190.134 --user=admin --password=Abc_123456 --port=3306 /home/backup
--defaults-file
:指定MySQL的配置文件所在路径,因为XtraBackup需要通过读取配置文件才能知道数据目录在哪--host
:指定MySQL服务的ip地址,因为全量备份时需要备份表结构,所以得连接到MySQL加读锁--user
:指定使用哪个MySQL用户进行备份--password
:MySQL用户的密码--port
:指定MySQL服务的端口号/home/backup
:备份文件存储的目录备份的文件目录如下:
[ ~]# ls /home/backup/ 2020-01-26_10-33-29 [ ~]# ls /home/backup/2020-01-26_10-33-29/ backup-my.cnf ibdata1 performance_schema test xtrabackup_binlog_info xtrabackup_info ib_buffer_pool mysql sys tpcc xtrabackup_checkpoints xtrabackup_logfile [ ~]#
以上这是一个最简单的全量热备份示例,可以看到备份出来的目录和文件有很多,并且也没有对这些备份文件进行压缩。如果需要备份的数据量比较大的话,不压缩备份文件就会很占用存储空间。但使用常规的压缩命令,如tar
、zip
等,则需要两次I/O操作,因为得先使用XtraBackup备份了数据之后,才能对产生的备份文件进行压缩。因此备份的数据量较大时,对系统I/O和CPU的影响就比较明显。
好在XtraBackup提供了流式压缩的功能,通过流式压缩可以将备份数据直接写到压缩文件中,而不用先备份再压缩,所以只需要一次I/O操作。如下:
想在备份时使用流式压缩只需要指定--stream
参数即可,如下示例:
[ ~]# innobackupex --defaults-file=/etc/my.cnf --host=192.168.190.134 --user=admin --password=Abc_123456 --port=3306 --no-timestamp --stream=xbstream -> /home/backup/backup.xbstream
--no-timestamp
:指定不生成时间戳目录--stream
:指定使用的压缩类型,目前只支持tar
和xbstream
/home/backup/backup.xbstream
:指定生成的压缩文件名称当备份一些涉及隐私的数据时,我们希望对备份文件进行加密,以防备份文件意外泄露时所带来的影响。而XtraBackup也提供了加密备份的功能,与加密相关的参数如下:
序号 | 参数 | 描述 |
---|---|---|
1 | --encrypt | 指定用于加密的算法:AES123、AES192、AES256 |
2 | --encrypt-threads | 指定执行加密的线程数 |
3 | --encrypt-chunk-size | 指定加密线程的缓存大小,默认64kb,大小不超过1M |
4 | --encrypt-key | 指定用于加解密的密钥字符串,长度至少24个字符 |
5 | --encrypt-key-file | 指定密钥文件的路径 |
加密备份示例:
[ ~]# innobackupex --defaults-file=/etc/my.cnf --host=192.168.190.134 --user=admin --password=Abc_123456 --port=3306 --no-timestamp --stream=xbstream --encrypt=AES256 --encrypt-threads=10 --encrypt-chunk-size 512 --encrypt-key=‘1K!cNoq&RUfQsY&&LAczTjco‘ -> /home/backup/encrypt-backup.xbstream
其他常用参数:
序号 | 参数 | 描述 |
---|---|---|
1 | --compress | 针对InnoDB数据文件进行压缩,可以与--stream 参数同时使用 |
2 | --compress-threads | 指定执行压缩的线程数 |
3 | --compress-chunk-size | 指定压缩线程的缓存大小,默认64kb,大小不超过1M |
4 | --include | 指定需要备份的数据表的正则表达式 |
5 | --galera-info | 指定备份PXC节点状态文件 |
使用示例:
[ ~]# innobackupex --defaults-file=/etc/my.cnf --host=192.168.190.134 --user=admin --password=Abc_123456 --port=3306 --no-timestamp --stream=xbstream --encrypt=AES256 --encrypt-threads=10 --encrypt-chunk-size 512 --encrypt-key=‘1K!cNoq&RUfQsY&&LAczTjco‘ --compress --compress-threads=10 --include=test.student,test.t_orders --galera-info -> /home/backup/backup2.xbstream
--include
:指明了仅备份test
库下的student
和t_orders
表,也可以写正则表达式上面介绍了全量热备份后,我们来看下如何将XtraBackup备份的文件进行还原。在还原这块只能冷还原,所谓冷还原就是得把数据库停机后进行还原。之所以不存在热还原,是因为对一个正在运行中的数据库进行在线还原操作,而同时用户又在读写数据,这就有可能导致数据互相覆盖,使得数据库的数据发生错乱。
因此,还原这块就只能是冷还原,本小节将逐步演示如何使用XtraBackup还原备份文件。首先关闭MySQL服务:
[ ~]# systemctl stop mysqld
清空数据目录及表分区的数据目录:
[ ~]# rm -rf /var/lib/mysql/* [ ~]# rm -rf /mnt/p0/data/* [ ~]# rm -rf /mnt/p1/data/*
rm
删除了,如果是实际的运行环境,建议先使用mv
重命名需要删除的目录,最后还原完备份文件并验证没有问题后,再使用rm
删除,以避免删库跑路的悲剧发生因为是热备份,所以事务日志中可能会存在一些未完成的事务,这就需要回滚没有提交的事务,以及同步已经提交的事务到数据文件。这里以2020-01-26_10-33-29
这个全量备份目录作为示例,执行如下命令:
[ ~]# innobackupex --apply-log /home/backup/2020-01-26_10-33-29/
然后使用以下命令进行备份文件的还原:
[ ~]# innobackupex --defaults-file=/etc/my.cnf --copy-back /home/backup/2020-01-26_10-33-29/
接着给还原后的目录文件赋予mysql
用户权限:
[ ~]# chown -R mysql:mysql /var/lib/mysql/* [ ~]# chown -R mysql:mysql /mnt/p0/data/* [ ~]# chown -R mysql:mysql /mnt/p1/data/*
到此为止就完成了冷还原,最后启动MySQL服务并自行验证下数据是否正常即可:
[ ~]# systemctl start mysqld
以上是对没有使用流式压缩,也没有使用加密的备份文件做的演示。如果是备份文件使用了流式压缩,则需要先使用xbstream
命令对其进行解压。如下示例:
# 创建解压后文件的存储目录 [ ~]# mkdir /home/backup/temp [ ~]# xbstream -x < /home/backup/backup2.xbstream -C /home/backup/temp/
如果备份文件使用了加密,则在解压之后还需要对其进行解密:
[ ~]# innobackupex --decompress --decrypt=AES256 --encrypt-key=‘1K!cNoq\&RUfQsY\&\&LAczTjco‘ /home/backup/temp
&
是特殊字符,所以需要使用\
转义一下。其中--decompress
是与--compress
对应的,用于解压被压缩的InnoDB数据文件,与解密无关增量热备份必须以全量热备份为基础进行备份,所以在了解了XtraBackup的全量热备份和全量冷还原后,接下来就可以实践XtraBackup的增量热备份了。
注意事项:
这里以2020-01-26_10-33-29
这个全量备份目录作为示例,增量热备份命令如下:
[ ~]# innobackupex --defaults-file=/etc/my.cnf --host=192.168.190.134 --user=admin --password=Abc_123456 --port=3306 --incremental-basedir=/home/backup/2020-01-26_10-33-29/ --incremental /home/backup/increment
--incremental-basedir
:指定全量备份文件所存储的目录,即基于哪个全量备份进行增量备份--incremental
:指定采用增量备份/home/backup/increment
:增量备份文件所存放的目录增量备份的文件目录如下:
[ ~]# ls /home/backup/increment/ 2020-01-26_17-02-21 [ ~]# ls /home/backup/increment/2020-01-26_17-02-21/ backup-my.cnf ibdata1.delta mysql sys tpcc xtrabackup_checkpoints xtrabackup_logfile ib_buffer_pool ibdata1.meta performance_schema test xtrabackup_binlog_info xtrabackup_info [ ~]#
可以使用du
命令对比一下全量热备份与增量热备份的目录大小:
[ ~]# du -sh /home/backup/increment/2020-01-26_17-02-21/ 3.3M /home/backup/increment/2020-01-26_17-02-21/ # 增量热备份的目录大小 [ ~]# du -sh /home/backup/2020-01-26_10-33-29/ 836M /home/backup/2020-01-26_10-33-29/ # 全量热备份的目录大小 [ ~]#
之后的第二次增量备份就可以不基于全量备份,而是基于第一次的增量备份,这样每次的增量备份都是一个备份点就像快照一样。如下示例:
[ ~]# innobackupex --defaults-file=/etc/my.cnf --user=admin --password=Abc_123456 --incremental-basedir=/home/backup/increment/2020-01-26_17-02-21/ --incremental /home/backup/increment
如果增量备份时需要使用流式压缩和内容加密,则添加相关参数即可。如下示例:
[ ~]# innobackupex --defaults-file=/etc/my.cnf --user=admin --password=Abc_123456 --incremental-basedir=/home/backup/increment/2020-01-26_17-02-21/ --incremental --stream=xbstream --encrypt=AES256 --encrypt-threads=10 --encrypt-chunk-size 512 --encrypt-key=‘1K!cNoq&RUfQsY&&LAczTjco‘ ./ > /home/backup/increment
./
表示将增量备份所有的内容都写到流式压缩文件里,压缩文件则存放在/home/backup/increment
目录下经过以上小节可以得知增量热备份仅备份新数据,并且生成的备份目录体积也要比全量热备份生成的目录体积要小很多。那么XtraBackup要如何将增量备份的数据还原到数据库呢?其实也很简单,就是先将增量热备份的数据与全量热备份的数据合并,然后基于合并后的备份数据去还原即可。
增量热备份可以有很多个备份点,因为除第一次增量热备份外,其余的增量热备份都是基于上一次增量热备份进行的。所以在还原的时候也可以选择任意一个备份点去还原,但事务日志的处理步骤与全量冷还原不一样。
在之前演示全量冷还原的时候,有一个处理事务日志的步骤,同样增量冷还原也有这个步骤,但是有些差异。上面提到增量热备份是可以有多个备份点的,那么在还原某一个备份点时就需要处理该备份点及其之前备份点的事务日志,否则就会出现数据混乱的情况。如下图,有三个备份点:
例如,当还原“增量备份1”时,需要先处理其前一个备份点的事务日志,即图中的“全量热备份”。接着再处理“增量备份1”这个备份点的事务日志,然后合并“增量备份1”的数据到“全量热备份”中。这样才能保证多个备份点合并到全量备份点后的数据是一致的,最后还原“全量热备份”中的数据即可。
再例如,要还原的是“增量备份2”,那么就得先处理“全量热备份”,然后处理“增量备份1”,接着处理“增量备份2”,按从前往后的顺序依次将这三个备份点的事务日志都给处理了后,才能合并备份点的数据到全量备份中,最后还原“全量热备份”中的数据。其余则以此类推......
接下来实操一下增量冷还原,这里有三个与上图对应的备份点目录:
/home/backup/2020-01-26_10-33-29/ # 全量热备份 /home/backup/increment/2020-01-27_10-11-24/ # 增量备份1 /home/backup/increment/2020-01-27_10-15-11/ # 增量备份2
因为是冷还原,所以得先关闭MySQL服务:
[ ~]# systemctl stop mysqld
在本例中要还原的是“增量备份2”这个备份点的数据,按照之前的说明,首先处理全量备份点的事务日志,执行如下命令:
[ ~]# innobackupex --apply-log --redo-only /home/backup/2020-01-26_10-33-29/
--redo-only
:指定不回滚未提交的事务,因为下个备份点的事务日志里可能会提交该备份点未提交的事务。如果回滚了就会导致下个备份点无法正常提交然后处理“增量备份1”的事务日志,并将"增量备份1"的数据合并到全量备份点上:
[ ~]# innobackupex --apply-log --redo-only /home/backup/2020-01-26_10-33-29/ --incremental-dir=/home/backup/increment/2020-01-27_10-11-24/
--incremental-dir
:指定要合并到全量备份的增量备份目录接着处理“增量备份2”的事务日志,并将"增量备份2"的数据合并到全量备份点上。由于只还原到“增量备份2”这个备份点,所以就不需要加上--redo-only
参数了,因为没有下个备份点了:
[ ~]# innobackupex --apply-log /home/backup/2020-01-26_10-33-29/ --incremental-dir=/home/backup/increment/2020-01-27_10-15-11/
与全量冷还原一样,也需清空数据目录及表分区的数据目录:
[ ~]# rm -rf /var/lib/mysql/* [ ~]# rm -rf /mnt/p0/data/* [ ~]# rm -rf /mnt/p1/data/*
完成以上步骤后,就可以使用如下命令完成备份文件的还原了:
[ ~]# innobackupex --defaults-file=/etc/my.cnf --copy-back /home/backup/2020-01-26_10-33-29/ # 注意这里指定的是全量备份点的目录
接着给还原后的目录文件赋予mysql
用户权限:
[ ~]# chown -R mysql:mysql /var/lib/mysql/* [ ~]# chown -R mysql:mysql /mnt/p0/data/* [ ~]# chown -R mysql:mysql /mnt/p1/data/*
到此为止还原就完成了,最后启动MySQL服务并自行验证下数据是否正常即可:
[ ~]# systemctl start mysqld