面试官:你知道MySQL的备份是怎么操作的吗
MySQL-基于XtraBackup实现逻辑备份
之前搞了一个基于XtraBackup进行逻辑备份MySQL数据库的,用了一段时间还不错,给大家分享一下。
XtraBackup是一个用于MySQL和Percona Server的开源热备份工具,它由Percona开发。XtraBackup支持两种备份类型:完整备份(full backup)和增量备份(incremental backup)。
备份原理:
- xtrabackup在备份过程中,会复制innodb的data file,由于是物理复制,因此能够在非常短的时间内完成备份。
- 对于MyISAM引擎的表,xtrabackup会进行COPY数据文件的方式进行备份。
- xtrabackup在备份过程中,会记录备份点,并且能够支持创建多个备份点。
- xtrabackup在备份innodb的同时,还会保存所有的binlog日志,用于恢复后应用。
- xtrabackup在备份过程中,会锁定表不能写入,因此对线上业务影响较大,建议在业务低峰时段进行备份。
恢复原理:
- xtrabackup在恢复过程中,会逐个应用binlog日志,并且重新应用redo log,最终恢复数据到指定的状态。
- 对于innodb表,xtrabackup会重新apply redo log。
- 对于MyISAM表,xtrabackup会COPY回原来的数据文件。
Linux XtraBackup安装
一、安装MySQL
1./etc/my.cnf这个里面要增加一条配置
[mysqld]
default-authentication-plugin = caching_sha2_password
innodb-read-only=OFF
default_storage_engine = InnoDB
二、手动下载XtraBackup安装包
地址:Software Downloads - Percona
注意:下载的版本最好是和MySQL版本一样,或者接近,不然会安装失败
三、安装XtraBackup
1.解压下载的安装包
tar -xvf Percona-XtraBackup-8.0.35-30-r6beb4b49-el7-x86_64-bundle.tar
2.安装XtraBackup
sudo yum install percona-xtrabackup-80-8.0.35-30.1.el7.x86_64.rpm
直接这个命令可能会安装失败:这个时候需要安装zstd包,需要使用yum安装的话直接
sudo yum install zstd
如果提示下面内容需要添加添加 EPEL 仓库
sudo yum install epel-release
添加完EPEL仓库后再安装zstd包即可
sudo yum install zstd
最后安装XtraBackup
从 EPEL 仓库手动下载 zstd 包:
wget [https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm](https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm "https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm")
sudo rpm -ivh epel-release-latest-7.noarch.rpm
安装 zstd 包:sudo yum install zstd
安装 Percona XtraBackup:sudo yum install percona-xtrabackup-80-8.0.35-30.1.el7.x86_64.rpm
最后查询安装的版本号校验是否安装成功:xtrabackup --version
四、编写脚本
1.脚本一,此脚本包含全量备份和增量备份,全量备份一天一次,增量备份一天23次,备份的目录结构如下:
# 获取当前日期格式为YYYY-MM-DD
# 创建的目录结构如下:
# -YYYY-MM-DD
# |-full
# |-inc1
# |-inc2
# ...
# |-incN
# 其中full为全量备份,inc1~N这些为增量备份,N为小时数。一天备份23次增量,一次全量。
#!/bin/bash
# 用于SecureTransport - 使用Percona XtraBackup在线备份MySQL的脚本
# 由Genata于2019年创建
# 确保有足够的备份空间。
# my.cnf 配置文件的目录,
FDH=/etc/my.cnf
# xtrabackup 的执行目录
hd=/usr/bin/
# 日志文件
ld=/usr/local/server/percona/logs
# 备份存放的文件夹
bd=/usr/local/server/percona/backup
# 获取当前日期,减去十五天
date_to_keep=$(date -d "15 days ago" +"%Y-%m-%d")
# 保存多少天的增量数据-无需保存,因为已经删除十五天之前的数据了
days=0
# 多线程的线程数
pp=4
# mysql 的账号密码
dbuser=username
dbpass=password
hn=$(uname -n)
if [ ! -d "$ld" ]; then
mkdir -p "$ld" # 如果日志目录不存在,创建它
fi
echo "[$(date -Iseconds)] 备份开始..." >>"$ld/ST_percona_backup.log"
date -Iseconds >"$ld/e_backup_percona"
# 获取当前日期格式为YYYY-MM-DD
# 创建的目录结构如下:
# -YYYY-MM-DD
# |-full
# |-inc1
# |-inc2
# ...
# |-incN
# 其中N为配置的小时数。一天备份23次增量,一次全量。
bp=$(date +%F) # 获取当前日期
if [ -d "$bd/$bp" ]; then
if [ -d "$bd/$bp/full" ]; then
echo "[$(date -Iseconds)] 今天 $bp 的全量备份已存在。" >>"$ld/ST_percona_backup.log"
echo "[$(date -Iseconds)] 备份完成。" >>"$ld/ST_percona_backup.log"
rm "$ld/e_backup_percona" 2>/dev/null
else
echo "[$(date -Iseconds)] 今天的全量备份不存在。删除 ${bp}。" >>"$ld/ST_percona_backup.log"
rm -r -f "$bd/$bp" # 如果今天的备份目录存在但没有全量备份,删除它
bp=""
fi
else
bp=""
fi
for i in $(seq 0 $days); do
bp1=$(date -d "-$i day" +%F)
if [ -d "$bd/$bp1" ]; then
bp=$bp1
bi=$i
break
fi
done
if [ -z "$bp" ]; then
# 执行全量备份
bp=$(date +%F)
echo "[$(date -Iseconds)] 将创建全量备份 $bd/$bp/full" >>"$ld/ST_percona_backup.log"
mkdir -p "$bd/$bp" # 创建今天的备份目录
echo "[$(date -Iseconds)] 开始全量备份 $bd/$bp/full" >>"$ld/ST_percona_xtrabackup.log"
${hd}xtrabackup --defaults-file="$FDH" --backup --user="$dbuser" --password="$dbpass" --parallel="$pp" --target-dir="$bd/$bp/full" >>"$ld/ST_percona_xtrabackup.log" 2>&1
pxe=$?
if [ $pxe -eq 0 ]; then
echo "[$(date -Iseconds)] 备份成功创建。主机名: $hn" >>"$ld/ST_percona_backup.log"
echo "$hn" >"$bd/$bp/full/backup_hostname"
chown -R mysql:mysql "$bd/$bp/" # 设置所有权
# 使用 find 命令查找并删除目标目录下所有文件名为时间格式的文件夹
find "$bd/" -maxdepth 1 -type d -name "????-??-??" -ctime +15 -exec rm -rf {} ;
echo "[$(date -Iseconds)] 删除15天前文件夹完成。" >>"$ld/ST_percona_backup.log"
else
rm -r -f "$bd/$bp/full"
echo "[$(date -Iseconds)] 创建备份时出错。xtrabackup退出代码: $pxe" >>"$ld/ST_percona_backup.log"
fi
else
# 执行增量备份
# 获取当前时间的小时部分
current_hour=$(date +%H)
if [ -d "$bd/$bp/full" ]; then
# 如果小时小于 10,则去掉前面的零
if [ $current_hour -lt 10 ]; then
current_hour=${current_hour#0}
# 如果小时等于0,直接退出脚本
if [ $current_hour -eq 0 ]; then
echo "[$(date -Iseconds)] 当前时间段为00:00:00-1:00:00之间,无需增量备份。" >>"$ld/ST_percona_backup.log"
exit 1 # 使用非零退出码表示错误或异常情况
fi
fi
# 循环查找最近存在的小时文件夹
for i in $(seq 1 $current_hour); do
# 检查是否存在该日期的文件夹
if [ -d "$bd/$bp/inc$i" ]; then
#存储距离当前时间的小时
bi=$i
fi
# 如果i等于当前小时,且退出循环
if [ $i -eq $current_hour ]; then
echo "[$(date -Iseconds)] 距离当前时间 $bi 小时的增量备份存在" >>"$ld/ST_percona_backup.log"
break
fi
done
inc_backup_dir="$bd/$bp/inc${current_hour}"
if [ -d "$inc_backup_dir" ]; then
echo "[$(date -Iseconds)] 今天在小时 $current_hour 的增量备份已存在。退出。" >>"$ld/ST_percona_backup.log"
echo "[$(date -Iseconds)] 备份完成。" >>"$ld/ST_percona_backup.log"
rm "$ld/e_backup_percona" 2>/dev/null
exit 0
else
# 执行增量备份
echo "[$(date -Iseconds)] 将从 $bd/$bp/full 创建增量备份 $inc_backup_dir" >>"$ld/ST_percona_backup.log"
mkdir -p "$inc_backup_dir" # 创建今天的小时级增量备份目录
chown -R mysql:mysql "$inc_backup_dir/" # 设置所有权
if [ -d "$bd/$bp/inc$bi" ]; then
#如果当前时间前面最近一个小时的增量备份存在,那么基于前面最近一个小时的增量备份数据做增量备份
echo "[$(date -Iseconds)] 开始从 $bd/$bp/inc$((current_hour-1)) 创建增量备份 $inc_backup_dir" >>"$ld/ST_percona_xtrabackup.log"
${hd}xtrabackup --defaults-file="$FDH" --backup --user="$dbuser" --password="$dbpass" --parallel="$pp" --target-dir="$inc_backup_dir" --incremental-basedir="$bd/$bp/inc$bi" >>"$ld/ST_percona_xtrabackup.log" 2>&1
else
#如果当前时间前面最近一个小时的增量备份不存在存在,那么基于全量备份数据做增量备份
echo "[$(date -Iseconds)] 开始从 $bd/$bp/full 创建增量备份 $inc_backup_dir" >>"$ld/ST_percona_xtrabackup.log"
${hd}xtrabackup --defaults-file="$FDH" --backup --user="$dbuser" --password="$dbpass" --parallel="$pp" --target-dir="$inc_backup_dir" --incremental-basedir="$bd/$bp/full" >>"$ld/ST_percona_xtrabackup.log" 2>&1
fi
pxe=$?
if [ $pxe -eq 0 ]; then
echo "[$(date -Iseconds)] 备份成功创建。主机名: $hn" >>"$ld/ST_percona_backup.log"
echo "$inc_backup_dir/backup_hostname"
# 使用 find 命令查找并删除目标目录下所有文件名为时间格式的文件夹
find "$bd/" -maxdepth 1 -type d -name "????-??-??" -ctime +15 -exec rm -rf {} ;
echo "[$(date -Iseconds)] 删除15天前文件夹完成。" >>"$ld/ST_percona_backup.log"
else
rm -r -f "$inc_backup_dir"
echo "[$(date -Iseconds)] 创建备份时出错。xtrabackup退出代码: $pxe" >>"$ld/ST_percona_backup.log"
fi
fi
else
echo "[$(date -Iseconds)] $bp 的全量备份不存在。删除 ${bp}。请再次运行此脚本。退出。" >>"$ld/ST_percona_backup.log"
rm -r -f "$bd/$bp"
echo "[$(date -Iseconds)] 备份完成。" >>"$ld/_ST_percona_backup.log"
rm "$ld/e_backup_percona" 2>/dev/null
exit 2
fi
fi
echo "[$(date -Iseconds)] 备份完成。" >>"$ld/ST_percona_backup.log"
rm "$ld/e_backup_percona" 2>/dev/null
exit $pxe
2.脚本二,此此脚本用于合并全量备份和增量备份的预处理,将合并后的数据保存在$bd/prep文件下,用于第三个脚本的恢复数据用。
#!/bin/bash
# 用于 SecureTransport 的脚本 - 准备在线 MySQL 备份以供 Percona XtraBackup 恢复
# 由 Genata 2019 创建
# 可以在另一台机器上执行准备,只要脚本对备份目录具有完全访问权限。
# 请注意,准备始终使用原始目录的副本。确保有足够的空间进行准备。
if [ -z "$1" ]; then
echo "用法: $(basename $0) <还原日期>"
exit 1
fi
# xtrabackup 的执行路径
hd=/usr/bin/
# 日志目录
ld=/usr/local/server/percona/logs
# 备份存放目录
bd=/usr/local/server/percona/backup
# 多线程数量
pp=4
# 用于替代 buffer_pool_size 的值。建议根据可用内存选择 1G、2G 或 4G。
um=1G
rd=`date -d "$1" +%F 2>/dev/null`
cr=$?
if [ "$cr" -gt 0 ]; then
echo 无效的还原日期
exit $cr
fi
#设置备份日期文件夹
bp=$rd
echo "还原日期为 $bp"
if [ -d "$bd/$bp" ]; then
echo 从 $bp 做全量备份预处理合并
echo "[`date -Iseconds`]" 全量备份预处理合并开始... >>$ld/ST_percona_prepare.log
rm -r -f "$bd/prep"
rm "$bd/prep_date" 2>/dev/null
cp -r "$bd/$bp/full" "$bd/prep"
echo 复制全量备份 $bd/$bp/full 到$bd/prep
echo ${hd}xtrabackup --prepare --use-memory=$um --target-dir="$bd/prep" >>$ld/ST_percona_prepare.log
${hd}xtrabackup --prepare --use-memory=$um --target-dir="$bd/prep" >>$ld/ST_percona_prep_xtrabackup.log 2>&1
pxe=$?
if [ $pxe -eq 0 ]; then
echo 全量备份 $rd 预处理成功。
echo "[`date -Iseconds`]" 全量备份 $rd 预处理合并成功。 >>$ld/ST_percona_prepare.log
echo $rd > "$bd/prep_date"
else
rm -r -f "$bd/prep"
rm "$bd/prep_date" 2>/dev/null
echo 全量备份处理合并时出错。xtrabackup 退出代码: $pxe
echo "[`date -Iseconds`]" 全量备份处理合并时出错。xtrabackup 退出代码: $pxe >>$ld/ST_percona_prepare.log
fi
echo "[`date -Iseconds`]" 全量备份处理合并完成。 >>$ld/ST_percona_prepare.log
else
bp=""
fi
echo "执行完全量备份预处理后的还原日期为 $bp"
# 循环查找最近存在的小时文件夹
for i in $(seq 1 23); do
# 检查是否存在该日期的文件夹
if [ -d "$bd/$bp/inc$i" ]; then
echo "距离当前时间 $i 小时的增量备份存在"
#存储距离当前时间的小时
bi=$i
fi
# 如果i等于23,且退出循环
if [ $i -eq 23 ]; then
echo "循环结束,此时存在$bi小时内的增量备份"
break
fi
done
if [ -z "$bp" ]; then
echo 无法找到从 $rd 的备份
echo "[`date -Iseconds`]" 无法找到从 $rd 的备份 >>$ld/ST_percona_prepare.log
exit 2
else
if [ -d "$bd/$bp/inc$bi" ]; then
echo "[$(date -Iseconds)] 输出距离当前时间 $bi 小时" >>"$ld/ST_percona_backup.log"
if [ -d "$bd/$bp/full" ]; then
echo 全量备份预处理 $bp ,增量备份预处理 $bi 次
echo "[`date -Iseconds`]" 增量备份预处理开始... >>$ld/ST_percona_prepare.log
rm -r -f "$bd/prep"
rm "$bd/prep_date" 2>/dev/null
cp -r "$bd/$bp/full" "$bd/prep"
echo 复制全量备份 $bd/$bp/full 到$bd/prep
chown -R mysql:mysql "$bd/$bp/" # 设置所有权
echo ${hd}xtrabackup --prepare --apply-log-only --use-memory=$um --target-dir="$bd/prep" >>$ld/ST_percona_prepare.log
${hd}xtrabackup --prepare --apply-log-only --use-memory=$um --target-dir="$bd/prep" >>$ld/ST_percona_prep_xtrabackup.log 2>&1
pxe=$?
if [ $pxe -eq 0 ]; then
echo "[`date -Iseconds`]" 增量备份步骤完全预处理成功。 >>$ld/ST_percona_prepare.log
else
rm -r -f "$bd/prep"
rm "$bd/prep_date" 2>/dev/null
echo 增量备份预处理完整步骤错误。xtrabackup 退出代码: $pxe
echo "[`date -Iseconds`]" 增量备份预处理完整步骤错误。xtrabackup 退出代码: $pxe >>$ld/ST_percona_prepare.log
echo "[`date -Iseconds`]" 增量备份预处理失败。 >>$ld/ST_percona_prepare.log
exit $pxe
fi
if [ "$bi" -gt "1" ]; then
for i in `seq 1 $bi`; do
if [ -d "$bd/$bp/inc$i" ]; then
rm -r -f "$bd/inc" 2>/dev/null
echo 删除增量备份 $bd/inc$(($i-1))在$bd/inc的复制文件
cp -r "$bd/$bp/inc$i" "$bd/inc"
echo 复制增量备份 $bd/$bp/inc$i 到$bd/inc
chown -R mysql:mysql "$bd/$bp/" # 设置所有权
echo ${hd}xtrabackup --prepare --apply-log-only --use-memory=$um --target-dir="$bd/prep" --incremental-dir=$bd/inc #$bp/inc$i >>$ld/ST_percona_prepare.log
${hd}xtrabackup --prepare --apply-log-only --use-memory=$um --target-dir="$bd/prep" --incremental-dir=$bd/inc >>$ld/ST_percona_prep_xtrabackup.log 2>&1
pxe=$?
if [ $pxe -eq 0 ]; then
echo 增量备份步骤 inc$i 准备成功。
echo "[`date -Iseconds`]" 增量备份步骤 inc$i 准备成功。 >>$ld/ST_percona_prepare.log
echo 增量备份预处理第$i 次成功执行了
else
rm -r -f "$bd/prep"
rm "$bd/prep_date" 2>/dev/null
rm -r -f "$bd/inc"
echo 增量备份预处理步骤 inc$i 时出错。xtrabackup 退出代码: $pxe
echo "[`date -Iseconds`]" 增量备份预处理步骤 inc$i 时出错。xtrabackup 退出代码: $pxe >>$ld/ST_percona_prepare.log
echo "[`date -Iseconds`]" 增量备份预处理失败。 >>$ld/ST_percona_prepare.log
echo 增量备份预处理第$i 次执行失败了
exit $pxe
fi
fi
done
fi
#代码--------------
echo 所有增量备份都成功合并。
echo "[`date -Iseconds`]" 所有增量备份都成功合并。 >>$ld/ST_percona_prepare.log
echo ${hd}xtrabackup --prepare --use-memory=$um --target-dir="$bd/prep" >>$ld/ST_percona_prepare.log
${hd}xtrabackup --prepare --use-memory=$um --target-dir="$bd/prep" >>$ld/ST_percona_prep_xtrabackup.log 2>&1
pxe=$?
if [ $pxe -eq 0 ]; then
echo 增量备份 $rd 准备成功。
echo "[`date -Iseconds`]" 增量备份 $rd 准备成功。 >>$ld/ST_percona_prepare.log
echo $rd > "$bd/prep_date"
else
rm -r -f "$bd/prep"
rm "$bd/prep_date" 2>/dev/null
echo 准备增量备份时出错。xtrabackup 退出代码: $pxe
echo "[`date -Iseconds`]" 准备增量备份时出错。xtrabackup 退出代码: $pxe >>$ld/ST_percona_prepare.log
fi
echo "[`date -Iseconds`]" 准备增量备份完成。 >>$ld/ST_percona_prepare.log
exit $pxe
else
echo 在 $bp 中找不到全量备份
echo "[`date -Iseconds`]" 在 $bp 中找不到全量备份 >>$ld/ST_percona_prepare.log
exit 2
fi
else
echo 输出 bi $bi
echo 无法找到从 $rd 的备份
echo "[`date -Iseconds`]" 无法找到从 $rd 的备份 >>$ld/ST_percona_prepare.log
exit 2
fi
fi
exit 0
3.脚本三,此脚本用于恢复预处理的数据至数据库,执行完该脚本后需要手动停止启动mysql。
#!/bin/bash
# SecureTransport - 使用 Parcona XtraBackup 恢复在线 MySQL 备份的脚本
# 由 Genata 2019 创建
# my.cnf 配置文件路径
FDH=/etc/my.cnf
# xtrabackup 执行文件目录
hd=/usr/bin/
# 日志文件地址
ld=/usr/local/server/percona/logs
# 备份存放目录
bd=/usr/local/server/percona/backup
# mysql数据库文件存放目录
mysqldatedir=/var/lib/mysql
mdd=$(grep datadir "$FDH" | cut -d "=" -f2)
bmd=mysql_$(date +%F)
if [ ! -d "$bd/prep" ]; then
echo "无法找到用于恢复的备份。请首先使用 percona_prepare.sh。"
exit 1
fi
pb=$(cat "$bd/prep_date" 2>/dev/null)
if [ -z "$pb" ]; then
echo "无法确定准备好的备份。请首先使用 percona_prepare.sh。"
exit 2
fi
if [ -d "$bd/$bmd" ]; then
while true; do
read -p "备份目录 $bd/$bmd 存在,并将被覆盖。
是否要继续? [y/N]" yn
case $yn in
[Yy]* ) rm -r -f "$bd/$bmd" 2>/dev/null; break;;
[Nn]* ) exit;;
* ) exit;;
esac
done
fi
hn=$(uname -n)
bhn=$(cat "$bd/prep/backup_hostname" 2>/dev/null)
if [ "$hn" != "$bhn" ]; then
while true; do
read -p "当前机器的主机名 "$hn" 与创建备份的机器的主机名 "$bhn" 不同。
在集群环境中,为每个节点单独创建备份,并在恢复时确保在相同的服务器上进行恢复。
某些配置选项是节点特定的,数据库在表 ConfigurationOption 中包含节点 ID。
如果在错误的节点上恢复数据库,ST 将启动但不会找到当前节点的特定选项,因此将无法正常工作。
是否要继续? [y/N]" yn
case $yn in
[Yy]* ) break;;
[Nn]* ) exit;;
* ) exit;;
esac
done
fi
echo "开始恢复..."
echo "[$(date -Iseconds)] 开始恢复..." >>"$ld/ST_percona_restore.log"
mpid=$(ps -ef | grep mysql | grep -v grep | awk '{print $2}' | tr "\n" " ")
if [ ! -z "$mpid" ]; then
$FDH/bin/stop_all
fi
mpid=$(ps -ef | grep mysql | grep -v grep | awk '{print $2}' | tr "\n" " ")
if [ ! -z "$mpid" ]; then
echo "kill -9 $mpid"
fi
cp -r "$mdd" "$bd/$bmd"
echo "备份 $mdd 至 $bd/$bmd 完成。"
echo "[$(date -Iseconds)] 备份 $mdd 至 $bd/$bmd 完成。" >>"$ld/ST_percona_restore.log"
rm -r -f "$mdd"
mkdir -p "$mdd"
${hd}xtrabackup --defaults-file="$FDH" --copy-back --target-dir="$bd/prep" >>"$ld/ST_percona_restore_xtrabackup.log" 2>&1
pxe=$?
if [ $pxe -eq 0 ]; then
echo "从备份 $pb 恢复完成。"
echo "[$(date -Iseconds)] 从备份 $pb 恢复完成。" >>"$ld/ST_percona_restore.log"
else
echo "从备份 $pb 恢复时出错。xtrabackup 退出代码: $pxe。回滚到先前版本的数据库 $bd/$bmd ..."
echo "[$(date -Iseconds)] 从备份 $pb 恢复时出错。xtrabackup 退出代码: $pxe。回滚到先前版本的数据库 $bd/$bmd ..." >>"$ld/ST_percona_restore.log"
rm -r -f "$mdd"
mkdir -p "$mdd"
cp -r "$bd/$bmd/*" "$mdd"
fi
echo "恢复完成。"
echo "[$(date -Iseconds)] 恢复完成。" >>"$ld/ST_percona_restore.log"
# 添加 chown 命令
chown -R mysql:mysql "$bd"
chown -R mysql:mysql "$mysqldatedir"
exit 0
4.执行顺序,脚本一->脚本二->脚本三
5.执行命令
sh ./ST_percona_backup.sh
sh ./ST_percona_prepare.sh date #任意输入时间的三种格式,或者直接输入date也行,一般输入“YYYY-MM-DD”格式
sh ./ST_percona_restore.sh
五、使用Linux自带的cron定时器跑脚本
1、将脚本移动到/etc/cron.hourly目录,这个是每小时执行一次的脚本目录,最快最方便。
2、给脚本赋予执行的权利
chmod +x /etc/cron.hourly/ST_percona_backup.sh
欢迎大家在评论区讨论,今天的干货分享就到此结束了,如果觉得对您有帮助,麻烦给个三连!
以上内容为本人的经验总结和平时操作的笔记。若有错误和重复请联系作者删除!!感谢支持!!
转载自:https://juejin.cn/post/7363674253168312360