xtrabackup工具备份和恢复

发布时间 2023-06-29 14:27:18作者: 灬夜露沁心灬

xtrabackup工具是Percona公司研发的一款开源、免费的Mysql热备份软件,还可以进行增量备份,在大数据量的场景下有明显的优势,也是我工作环境中常用的备份工具(跟mysqldump类似的是还是需要配合binlog的备份来做到尽可能多的恢复)

1、xtrabackup工具安装

根据官方给的说明文档进行下载安装,我使用的是5.7的mysql对应的xtrabackup需要用2.4版本的,官方给出了多种安装方式,这里就选用yum的方式:

yum install https://repo.percona.com/yum/percona-release-latest.noarch.rpm
yum list | grep percona
yum install percona-xtrabackup-24
yum install qpress

[NOTE]
tar包下载:wget https://downloads.percona.com/downloads/Percona-XtraBackup-2.4/Percona-XtraBackup-2.4.27/binary/tarball/percona-xtrabackup-2.4.27-Linux-x86_64.glibc2.12.tar.gz,文档中也有说明,后续解压安装。

2、常用参数说明

  • -v, --version 查看版本信息
  • -u, --user=name 指定用户名
  • -H, --host=name 指定主机
  • -P, --port=# 指定端口
  • -p, --password[=name] 指定密码
  • --backup 表⽰该操作为备份操作
  • --no-defaults和--defaults-file=# 指定配置文件 ,如果不指定–defaults-file,默认值为/etc/my.cnf
  • --target-dir=name 指定备份⽂件的路径
  • --compress[=name]和--decompress 备份压缩
  • --parallel=# 并行个数,根据主机配置选择合适的,默认是1个,多个可以加快备份速度
  • --slave-info 在备份从库时会常用,会记录源服务器的binlog position,并直接将CHANGE MASTER命令写入extrabackup_slave_info文件,便于从库恢复后直接恢复主从配置
  • --apply-log-only 仅做日志写入数据文件操作,跳过回滚阶段,常在增量备份恢复时使用
  • --incremental-basedir=name 指定包含完全备份的目录
  • --incremental-dir=name 指定包含增量备份的目录
  • --prepare 准备恢复
  • --copy-back和--move-back 恢复备份目录

[NOTE]
更多的参数说明可以通过xtrabackup --help或者官方说明文档

3、xtrabackup用户权限

如果是单独配置的用于执行xtrabackup的linux用户也需要注意用户要有对应备份目录的wrx权限,对datadir目录要rx权限。不过我们一般会直接使用root用户进行操作,这里就只要注意数据库中的用户权限配置,具体在官方文档中也有说明:

mysql> CREATE USER 'xtrabackupuser'@'localhost' IDENTIFIED BY 'xtrabackup@123';
mysql> GRANT RELOAD, LOCK TABLES, PROCESS, REPLICATION CLIENT, CREATE TABLESPACE, SUPER ON *.* TO 'xtrabackupuser'@'localhost';
mysql> GRANT CREATE, ALTER, INSERT, SELECT ON PERCONA_SCHEMA.* TO 'xtrabackupuser'@'localhost';
mysql> FLUSH PRIVILEGES;

这里再给一个使用最小权限的用户进行备份的权限配置:

mysql> GRANT RELOAD,LOCK TABLES,REPLICATION CLIENT,PROCESS ON *.* TO 'xtrabackupuser'@'localhost';

4、备份和恢复

  • 全备和恢复
    (1) 执行全备:xtrabackup --user=xtrabackupuser --password=xtrabackup@123 --backup --target-dir=/database/mysql/backup/,备份完成后可以在本分目录中的xtrabackup_info文件中查看此次备份的相关信息。
    (2) 准备恢复备份:在做恢复前需要先“准备”,xtrabackup --prepare --target-dir=/database/mysql/backup/
    (3) 恢复:xtrabackup --copy-back --target-dir=/database/mysql/backup/,然后确认数据库目录以及文件的属组问题,没有问题之后再重启启动数据库检查数据是否恢复。

    [NOTE]
    没有指定defaults-file参数,即使用默认的/etc/my.cnf,在做备份和恢复时xtrabackup会从/etc/my.cnf中读取datadir。

  • 增量备份
    (1)增量备份是基于一次全备的基础上,主要是通过--incremental-basedir参数来定位,在做增量备份前我们先创建好两个目录../full和../incr来分别存储备份文件。
    (2) 先做一次全备:xtrabackup --user=xtrabackupuser --password=xtrabackup@123 --backup --target-dir=/database/mysql/backup/full
    (3) 数据库发生修改等变化,然后基于前面的全备做一次增备:xtrabackup --user=xtrabackupuser --password=xtrabackup@123 --backup --target-dir=/database/mysql/backup/incr --incremental-basedir=/database/mysql/backup/full
    (4) 基于上次的增量备份我们再做一次增量备份:xtrabackup --user=xtrabackupuser --password=xtrabackup@123 --backup --target-dir=/database/mysql/backup/incr2 --incremental-basedir=/database/mysql/backup/incr
    (5)准备恢复增量备份:于全备恢复的准备不同的时,这里需要加上--apply-log-only参数,全备准备恢复时是执行两种类型的操作以使数据库保持一致:已提交的事务从日志文件写入数据文件,未提交的事务回滚。在准备增量备份时,必须跳过未提交事务的回滚,因为备份时未提交的事务可能正在进行中,而且很可能会在下一次增量备份中提交。多次增量备份,最后一次则无需跳过回滚阶段。从全备开始依次为三个目录做准备,xtrabackup --prepare --apply-log-only --target-dir=/database/mysql/backup/fullxtrabackup --prepare --apply-log-only --target-dir=/database/mysql/backup/full --incrmental-dir=/database/mysql/backup/incrxtrabackup --prepare --target-dir=/database/mysql/backup/full --incrmental-dir=/database/mysql/backup/incr2
    (6)恢复:最后恢复于全备基本相同即将最终准备好的全备目录copy到数据库目录,修改属组权限后启动数据库,xtrabakcup --copy-back --target-dir=/database/mysql/backup/full

    [NOTE]
    不支持使用相同的增量备份目录来准备两个备份副本,所以每次的incremental-dir应该是都是不同的且依次递增。

5、自动备份脚本

配合服务器定时任务,预设在每日凌晨2点执行一次,将脚本放在/database/scripts目录下:
crontab -e 0 2 * * * /database/scripts/xtrabackup.sh

脚本内容如下:

#!/bin/sh
# on xtrabackup 2.4.8
# 第一次执行是会检查是否已经有全备,没有的话则进行一次全备
# 后续再执行时会根据脚本的设定来判断执行增量备份或者新的一轮全备以及增备
# 一天最多执行一次该脚本,若想同一天多次执行需要修改增量备份目录命名参数
# auth:cuckoo

XTRABACKUPEX=/usr/bin/xtrabackup  #yum安装后xtrabackup命令的绝对路径

# 备份目标数据库的信息

MYSQL_INF='--host=localhost --user=xtrabackupuser --password=xtrabackup@123 --port=3306'  # 目标数据库信息
MY_CNF='/etc/my.cnf'  # 数据库配置文件

# 备份存储信息

BACKUP_DIR=/database/backup   # 备份主目录
FULLBACKUP_DIR=$BACKUP_DIR/full  # 全库备份的目录 
INCRBACKUP_DIR=$BACKUP_DIR/incr   # 增量备份的目录
FULLBACKUP_INTERVAL=604800  # 全库备份的间隔周期,时间:秒
KEEP_FULLBACKUP=1 # 至少保留几个全库备份

# 日志信息

LOG_DIR=$BACKUP_DIR/log
LOGFILE=$LOG_DIR/main_log.log
DETAILD_LOG=$LOG_DIR/backup_`date +%Y%m%d`.log
TMP_LOG=$LOG_DIR/tmplog_`date +%Y%m%d`.log

# 检测备份目录是否存在

if [ ! -d $BACKUP_DIR ] ; then
	mkdir -p $BACKUP_DIR/{full,incr,log}
	echo -e "检测到备份目录$BACKUP_DIR不存在,自行完成创建。"
fi

if [ ! -d $FULLBACKUP_DIR ] ; then
	mkdir -p $FULLBACKUP_DIR
	echo -e "检测到备份目录$FULLBACKUP_DIR不存在,自行完成创建。"
fi

if [ ! -d $INCRBACKUP_DIR ] ; then
	mkdir -p $INCRBACKUP_DIR
	echo -e "检测到备份目录$INCRBACKUP_DIR不存在,自行完成创建。"
fi

if [ ! -d $LOG_DIR ] ; then
	mkdir -p $LOG_DIR
	echo -e "检测到备份目录$LOG_DIR不存在,自行完成创建。"
fi

# 开始时间

STARTED_TIME=`date +%s`

# 生成空白日志文件

touch $LOGFILE
touch $DETAILD_LOG

# 开始备份

## 备份头部信息

echo -e "-------------------------------------------------------------------------------------------------------------" >> $LOGFILE
echo >> $LOGFILE
echo -e "$0: MySQL备份脚本" >> $LOGFILE
echo -e "\e[31m 开始于: `date +%Y%m%d%H%M` \e[m" >> $LOGFILE
echo >> $LOGFILE

# 查找最新的完全备份

LATEST_FULL_BACKUP=`find $FULLBACKUP_DIR -mindepth 1 -maxdepth 1 -type d -printf "%P\n" | sort -nr | head -1`
  
# 查找最近求完全备份的创建时间
 
LATEST_FULL_BACKUP_CREATED_TIME=`stat -c %Y $FULLBACKUP_DIR/$LATEST_FULL_BACKUP`
 
 
# 如果全备有效进行增量备份否则执行完全备份
if [ "$LATEST_FULL_BACKUP" -a `expr $LATEST_FULL_BACKUP_CREATED_TIME + $FULLBACKUP_INTERVAL + 5` -ge $STARTED_TIME ] ; then
	# 如果最新的全备未过期则以最新的全备文件名在增量备份目录下新建目录
	echo -e "完全备份$LATEST_FULL_BACKUP未过期,将根据$LATEST_FULL_BACKUP创建增量备份基础目录名"  >> $LOGFILE
	NEW_INCRDIR=$INCRBACKUP_DIR/$LATEST_FULL_BACKUP
	if [ ! -d $NEW_INCRDIR ] ; then
		mkdir -p $NEW_INCRDIR
	fi
 
	# 查找最新的增量备份是否存在.指定一个备份的路径作为增量备份的基础
	LATEST_INCR_BACKUP=`find $NEW_INCRDIR -mindepth 1 -maxdepth 1 -type d -printf "%P\n"  | sort -nr | head -1`
		if [ ! $LATEST_INCR_BACKUP ] ; then
			NEW_INCR_DATE=`date +%Y%m%d`
			NEW_INCR_DIR=$NEW_INCRDIR/$NEW_INCR_DATE
			mkdir -p $NEW_INCR_DIR
			INCRBASEDIR=$FULLBACKUP_DIR/$LATEST_FULL_BACKUP
			echo -e "增量备份将以$INCRBASEDIR作为备份基础目录"  >> $LOGFILE
		else
			NEW_INCR_DATE=`date +%Y%m%d`
			NEW_INCR_DIR=$NEW_INCRDIR/$NEW_INCR_DATE
			mkdir -p $NEW_INCR_DIR
			INCRBASEDIR=$INCRBACKUP_DIR/${LATEST_FULL_BACKUP}/${LATEST_INCR_BACKUP}
			echo -e "增量备份将以$INCRBASEDIR作为备份基础目录"  >> $LOGFILE
		fi
 
	echo "使用$INCRBASEDIR作为本次增量备份的基础目录开始进行备份----"   >> $LOGFILE
	echo  "*************************************"
	echo -e "正在使用$INCRBASEDIR作为本次增量备份的基础目录开始进行备份.....请稍等....."
	echo  "*************************************"
	$XTRABACKUPEX --defaults-file=$MY_CNF --use-memory=4G $MYSQL_INF --backup --target-dir=$NEW_INCR_DIR --incremental-basedir=$INCRBASEDIR >> $TMP_LOG 2>&1
 
 	#保留一份备份的详细日志
 
	cat $TMP_LOG > $DETAILD_LOG
 
	if [ -z "`tail -1 $TMP_LOG | grep 'completed OK!'`" ] ; then
	 echo -e "$XTRABACKUPEX命令执行失败:" >> $LOGFILE
	 echo -e "可以在$DETAILD_LOG中查看详细信息。"  >> $LOGFILE
	 echo "					   "  >> $LOGFILE
	 rm -f $TMP_LOG
	 echo -e "-------------------------------------------------------------------------------------------------------------" >> $LOGFILE
	 exit 1
	fi
 
	rm -f $TMP_LOG
 
	echo -e "数据库成功备份到:$NEW_INCR_DIR"  >> $LOGFILE
 
	# 提示应该保留的备份文件起点
 
	RES_FULL_BACKUP=${FULLBACKUP_DIR}/${LATEST_FULL_BACKUP}
	RES_INCRE_BACKUP=${NEW_INCRDIR}
 
	echo -e "\e[31m 含有增量备份,必须保留$KEEP_FULLBACKUP份全备即全备${RES_FULL_BACKUP}和以此全备为基础的所有增量备份即${RES_INCRE_BACKUP}目录中所有增量备份 \e[m" >> $LOGFILE
	echo "					   "  >> $LOGFILE
 
else
	if [ ! $LATEST_FULL_BACKUP ] ; then
		echo -e "检测到还未进行过完全备份,将创建首次完全备份"  >> $LOGFILE
	else
		echo -e "完全备份$LATEST_FULL_BACKUP已过期,将创建新的完全备份"  >> $LOGFILE
	fi
	echo  "*************************************"
	echo -e "正在创建全新的完全备份目录...请稍等..."
	echo  "*************************************"
	NEW_FULLBACKUP_DATE=`date +%Y%m%d`
	NEW_FULLBACKUP_DIR=$FULLBACKUP_DIR/$NEW_FULLBACKUP_DATE
	mkdir -p $NEW_FULLBACKUP_DIR

	echo  "*************************************"
	echo -e "正在执行全新的完全备份.....请稍等....."
	echo  "*************************************"
	$XTRABACKUPEX --defaults-file=$MY_CNF --use-memory=4G $MYSQL_INF --backup --target-dir=$NEW_FULLBACKUP_DIR >> $TMP_LOG 2>&1

	#保留一份备份的详细日志
 
	cat $TMP_LOG > $DETAILD_LOG
 
	if [ -z "`tail -1 $TMP_LOG | grep 'completed OK!'`" ] ; then
	 echo -e "$XTRABACKUPEX命令执行失败:" >> $LOGFILE
	 echo -e "可以在$DETAILD_LOG中查看详细信息。"  >> $LOGFILE
	 echo "					   "  >> $LOGFILE
	 rm -f $TMP_LOG
	 echo -e "-------------------------------------------------------------------------------------------------------------" >> $LOGFILE
	 exit 1
	fi
 
	rm -f $TMP_LOG
 
	echo -e "数据库成功备份到:$NEW_FULLBACKUP_DIR"  >> $LOGFILE
 
	# 提示应该保留的备份文件起点
 
	LATEST_FULL_BACKUP=`find $FULLBACKUP_DIR -mindepth 1 -maxdepth 1 -type d -printf "%P\n" | sort -nr | head -1`
 
	RES_FULL_BACKUP=${FULLBACKUP_DIR}/${LATEST_FULL_BACKUP}
 
	echo -e "\e[31m 无增量备份,必须保留$KEEP_FULLBACKUP份全备即全备${RES_FULL_BACKUP}. \e[m" >> $LOGFILE
	echo "					   "  >> $LOGFILE
 
fi

 
# 删除过期的备份文件,保留2周
 
echo -e "find expire backup file...........waiting........."
echo -e "寻找过期的备份文件并删除" >> $LOGFILE
EFULLBACKUPDIR=`/usr/bin/find $FULLBACKUP_DIR/ -mindepth 1 -maxdepth 1 -mtime +21 -type d -print`  # 预设每周日全备,周一到周六增备
if [ ! ${EFULLBACKUPDIR} ] ; then
	echo -e "\e[31m 本次未找到可以删除的过期备份文件 \e[m" >> $LOGFILE
	echo "					   "  >> $LOGFILE
else
	rm -rf ${EFULLBACKUPDIR}
	EINCRBACKUPDIR=`awk '{split($EFULLBACKUPDIR,a,"/");print a[6]}'`
	rm -rf ${INCRBACKUP_DIR}/${EINCRBACKUPDIR}
	echo -e "\e[31m 已删除过期全备:${EFULLBACKUPDIR}目录及文件和其对应的增量备份 \e[m" >>$LOGFILE
	echo "					   "  >> $LOGFILE
fi
echo -e "Finished deleting expired backup files."



# 本次备份结束

echo -e "\e[31m 结束于: `date +'%F %T %w'` \e[m" >> $LOGFILE
echo -e "$0:本次备份结束,可以在$LOGFILE和$DETAILD_LOG中查看本次备份信息。" >> $LOGFILE
echo -e "$0:本次备份结束,可以在$LOGFILE和$DETAILD_LOG中查看本次备份信息。"
echo >> $LOGFILE
echo -e "-------------------------------------------------------------------------------------------------------------" >> $LOGFILE
echo >> $LOGFILE
echo >> $LOGFILE
exit 0