数据库热备份神器 - XtraBackup

常见的热备份方案

LVM方案

利用Linux的LVM技术来实现热备份,将MySQL的数据目录放到LVM逻辑卷上,然后通过LVM快照技术备份逻辑卷的内容。第一次备份是全量备份,之后的备份都是增量备份。在还原时,将快照中的数据目录恢复到ySQL的数据目录即可。

使用LVM这种技术不仅可以备份MySQL还可以备份MongoDB等其他数据库,但使用LVM做热备份方案也比较麻烦,因为需要手动创建逻辑卷、迁移数据目录、创建快照以及给数据库加锁等等,所以LVM并不是常用的热备份方案。

XtraBackup方案

因为LVM的麻烦,所以人们都希望使用专业的工具去做热备份,这个工具就是XtraBackup。XtraBackup是由Percona开源的免费数据库热备份工具,它能对InnoDB数据库和XtraDB存储引擎的数据库非阻塞地备份。因为XtraBackup在备份过程中不会打断正在执行的事务,而事务日志中记录了哪些是备份前写入的数据哪些是备份后写入的数据,所以无需加锁。

另外,XtraBackup提供了对备份数据的压缩功能,可以节约备份文件占用的磁盘空间及网络带宽。但XtraBackup在备份使用MyISAM作为存储引擎的表时会加读锁,即表中的数据可读但不可写,不过这也不是问题,之前提到了可以使用联机热备份的方式来解决加读锁的问题。同样,XtraBackup支持全量备份和增量备份,因为XtraBackup的方便性,所以一般都是采用XtraBackup来做热备份方案。

XtraBackup热备份原理

因为XtraBackup是主流的MySQL热备份方案,所以这里简单介绍一下XtraBackup热备份原理:

  • XtraBackup是一种物理备份工具,通过协议连接到MySQL服务端,然后读取并复制底层的数据文件,完成物理备份。需要注意的是XtraBackup支持对InnoDB做全量备份和增量备份,但只能对MyISAM做全量备份
    数据库热备份神器 - XtraBackup

现在已经知道了XtraBackup是通过读取并复制底层的数据文件,完成物理备份的。其中全量备份比较简单,直接备份数据文件中的所有内容即可。而增量备份则需要区分新数据和旧数据,然后仅备份新数据,所以稍微复杂以些。

XtraBackup增量备份的原理如下:

增量备份只备份新增的数据,所以XtraBackup会去读取数据文件中的内容来判断哪些是旧数据哪些是新数据,然后只去备份新数据。在MySQL的数据文件里,数据是存放在row这种结构中的,而row存放在page中,page则存在于extend中。

MySQL会为每一个page都标记上一个LSN编号,通过对比该编号就可以得知哪些数据是新的,哪些数据是旧的。然后XtraBackup只需要从数据文件中,将这些新的page数据备份出来就行了。

示意图:

数据库热备份神器 - XtraBackup

那么XtraBackup是如何得知哪些LSN是新的呢?首先我们要知道LSN是一个全局递增的编号,每次对page中的数据进行修改时都会产生新的LSN编号。假设现在有6个page,各自的LSN编号如下:
数据库热备份神器 - XtraBackup

上图中,LSN编号为3的表示被修改过3次,LSN编号为5的表示被修改过5次,以此类推。假设此时对这些page进行一次全量备份,那么这6个page都会被备份下来。经过一段时间后,其中有三个page被修改了,LSN编号发生了变化,如下所示:
数据库热备份神器 - XtraBackup

当进行增量备份时,XtraBackup就会将之前备份的pageLSN编号与数据文件中与之对应的pageLSN编号进行对比,若数据文件里的LSN编号大于备份里的LSN编号则代表数据文件中的page是新数据,那么XtraBackup就会对其进行备份。若等于则代表数据没有发生变化,不进行备份。因为LSN编号是全局递增的,所以不存在小于的情况。这就是XtraBackup增量备份的原理。


安装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命令种类:

序号命令描述
1xbcrypt用于加密或解密备份的数据
2xbstream用于压缩或者解压缩xbstream文件
3xtrabackup用于备份使用InnoDB、XtraDB作为存储引擎的数据表,该命令仅备份表数据文件,所以通常用在增量备份上
4innobackupex是上面三种命令的perl脚本封装,可以使用该命令备份MyISAM数据表,并且只有使用该命令才能备份表数据文件、表定义文件、表索引文件,所以通常用在全量备份上

全量热备份

使用innobackupex命令备份InnoDB数据表的流程图:
数据库热备份神器 - XtraBackup

使用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
[ ~]#

以上这是一个最简单的全量热备份示例,可以看到备份出来的目录和文件有很多,并且也没有对这些备份文件进行压缩。如果需要备份的数据量比较大的话,不压缩备份文件就会很占用存储空间。但使用常规的压缩命令,如tarzip等,则需要两次I/O操作,因为得先使用XtraBackup备份了数据之后,才能对产生的备份文件进行压缩。因此备份的数据量较大时,对系统I/O和CPU的影响就比较明显。

好在XtraBackup提供了流式压缩的功能,通过流式压缩可以将备份数据直接写到压缩文件中,而不用先备份再压缩,所以只需要一次I/O操作。如下:
数据库热备份神器 - XtraBackup

想在备份时使用流式压缩只需要指定--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:指定使用的压缩类型,目前只支持tarxbstream
  • /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库下的studentt_orders表,也可以写正则表达式

全量冷还原

上面介绍了全量热备份后,我们来看下如何将XtraBackup备份的文件进行还原。在还原这块只能冷还原,所谓冷还原就是得把数据库停机后进行还原。之所以不存在热还原,是因为对一个正在运行中的数据库进行在线还原操作,而同时用户又在读写数据,这就有可能导致数据互相覆盖,使得数据库的数据发生错乱。

因此,还原这块就只能是冷还原,本小节将逐步演示如何使用XtraBackup还原备份文件。首先关闭MySQL服务:

[ ~]# systemctl stop mysqld

清空数据目录及表分区的数据目录:

[ ~]# rm -rf /var/lib/mysql/*
[ ~]# rm -rf /mnt/p0/data/*
[ ~]# rm -rf /mnt/p1/data/*
  • Tips:这里由于是示例就直接使用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
  • Tips:因为&是特殊字符,所以需要使用\转义一下。其中--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
  • Tips:这里的./表示将增量备份所有的内容都写到流式压缩文件里,压缩文件则存放在/home/backup/increment目录下

增量冷还原

经过以上小节可以得知增量热备份仅备份新数据,并且生成的备份目录体积也要比全量热备份生成的目录体积要小很多。那么XtraBackup要如何将增量备份的数据还原到数据库呢?其实也很简单,就是先将增量热备份的数据与全量热备份的数据合并,然后基于合并后的备份数据去还原即可。

增量热备份可以有很多个备份点,因为除第一次增量热备份外,其余的增量热备份都是基于上一次增量热备份进行的。所以在还原的时候也可以选择任意一个备份点去还原,但事务日志的处理步骤与全量冷还原不一样。

在之前演示全量冷还原的时候,有一个处理事务日志的步骤,同样增量冷还原也有这个步骤,但是有些差异。上面提到增量热备份是可以有多个备份点的,那么在还原某一个备份点时就需要处理该备份点及其之前备份点的事务日志,否则就会出现数据混乱的情况。如下图,有三个备份点:
数据库热备份神器 - 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

相关推荐