简介
宝塔计划任务里面有个数据库差异备份,很好用,但是竟然需要购买才可以使用,在经过几天的调研,用第三方免费工具组合做了一个脚本
前置条件
- 机器有Docker
- 机器配置建议2核心2G内存以上
- 安装宝塔(可选)
注意
- 当前只对MySQL 5.7进行了测试
- 当前脚本里面的一些变量为宝塔的默认设置,如果不使用宝塔自行修改变量里面的路径
使用
# 1 配置信息:
# 1.1 数据库用户密码:修改脚本中 MYSQL_USER 和 MYSQL_PASSWORD 变量为实际的数据库用户和密码。
# 1.2 备份目录:修改脚本中 BACKUP_DIR 变量为实际的备份目录。
# 1.3 全量备份间隔天数:修改脚本中 FULL_BACKUP_INTERVAL 变量为实际的全量备份间隔天数。
# 1.4 Xtrabackup 镜像:修改脚本中 XTRABACKUP_IMAGE 变量为实际的 Xtrabackup 镜像。
# 1.5 MySQL 数据目录:修改脚本中 MYSQL_DATA_DIR 变量为实际的 MySQL 数据目录。
# 2 备份脚本执行:
# 2.1 全量备份:脚本会自动执行全量备份,并记录备份日期到 $LAST_FULL_BACKUP_FILE 文件。
# 2.2 增量备份:脚本会自动执行增量备份,备份日期为脚本执行时刻。
# 2.3 准备备份数据:脚本会自动准备备份数据,包括全量备份和多份增量备份。
# 2.4 恢复备份:脚本会自动恢复备份,恢复时会停止 MySQL 服务,清空 MySQL 数据目录,并恢复备份数据。
# 3 备份脚本定时执行:
# 3.1 使用 crontab 定时任务调度脚本:
# 3.1.1 编辑 crontab:crontab -e
# 3.1.2 添加定时任务:添加以下内容,每天凌晨 2 点执行备份脚本:
# 0 2 * * * /path/to/backup_script.sh
# 3.1.3 保存并退出:保存并退出。
# 3.1.4 验证定时任务:运行 crontab -l 命令,查看定时任务是否添加成功。
#----------------------------------------------------------
# 1 给脚本添加执行权限:
# chmod +x /path/to/backup_script.sh
# 2 使用 cron 定时任务调度脚本:
# crontab -e
# 添加以下内容,每天凌晨 2 点执行备份脚本:
# 0 2 * * * /path/to/backup_script.sh
#----------------------------------------------------------
# 示例调用准备备份数据和恢复备份
# 恢复1 准备包含多份增量备份的情况 准备备份数据:调用 prepare_backup 函数,第一个参数为全量备份目录,后续参数依次为要合并的增量备份目录,按备份时间顺序排列。
# prepare_backup "$FULL_BACKUP_DIR/20250502120000" "$INCREMENTAL_BACKUP_DIR/20250503120000" "$INCREMENTAL_BACKUP_DIR/20250504120000"
# 恢复2 恢复备份:调用 restore_backup 函数,参数为准备好的全量备份目录。
# restore_backup "$FULL_BACKUP_DIR/20250502120000"
脚本内容
#!/bin/bash
# www.wanji365.com 出品
# 配置信息
MYSQL_USER="root" # 数据库用户 建议使用 root 用户
MYSQL_PASSWORD="yourpassword" # 数据库密码
BACKUP_DIR="/backup" # 备份目录
FULL_BACKUP_DIR="$BACKUP_DIR/full_backup" # 全量备份目录
INCREMENTAL_BACKUP_DIR="$BACKUP_DIR/incremental_backup" # 增量备份目录
LAST_FULL_BACKUP_FILE="$BACKUP_DIR/last_full_backup" # 上次全量备份文件
FULL_BACKUP_INTERVAL=7 # 全量备份间隔天数
XTRABACKUP_IMAGE="percona/percona-xtrabackup:2.4" # Xtrabackup 镜像 2.4 版本 对应 MySQL 5.7
MYSQL_DATA_DIR="/www/server/data" # MySQL 数据目录
MYSQL_BASE_DIR="/www/server/mysql" # MySQL 基础目录
MYSQL_CONFIG_FILE="/etc/my.cnf" # MySQL 配置文件
MYSQL_PORT="3306" # MySQL 端口
MYSQL_SOCKET="/tmp/mysql.sock" # MySQL 套接字
# 执行备份的通用函数
run_backup() {
local target_dir="$1"
local incremental_basedir="$2"
#local cmd="docker run --rm --privileged --network=host \ # 如果要在宝塔的计划任务里面运行,需要使用本行
local cmd="docker run --rm -it --privileged --network=host \
-v \"$BACKUP_DIR\":\"$BACKUP_DIR\" \
-v \"$MYSQL_CONFIG_FILE\":/etc/my.cnf:ro \
-v \"$MYSQL_SOCKET\":\"$MYSQL_SOCKET\":ro \
-v \"$MYSQL_BASE_DIR\":\"$MYSQL_BASE_DIR\":ro \
-v \"$MYSQL_DATA_DIR\":\"$MYSQL_DATA_DIR\":rw \
\"$XTRABACKUP_IMAGE\" \
xtrabackup --backup \
--host=localhost \
--user=\"$MYSQL_USER\" \
--password=\"$MYSQL_PASSWORD\" \
--port=\"$MYSQL_PORT\" \
--socket=\"$MYSQL_SOCKET\" \
--target-dir=\"$target_dir\""
if [ -n "$incremental_basedir" ]; then
cmd="$cmd --incremental-basedir=\"$incremental_basedir\""
fi
echo "执行命令: $cmd"
eval $cmd
return $?
}
# 全量备份函数
full_backup() {
local backup_date=$(date +%Y%m%d%H%M%S)
local target_dir="$FULL_BACKUP_DIR/$backup_date"
if run_backup "$target_dir" ""; then
echo "$backup_date" > "$LAST_FULL_BACKUP_FILE"
echo "全量备份成功,备份目录: $target_dir"
else
echo "全量备份失败"
exit 1
fi
}
# 增量备份函数
incremental_backup() {
if [ ! -f "$LAST_FULL_BACKUP_FILE" ]; then
echo "未找到全量备份记录,请先进行全量备份。"
exit 1
fi
local last_full_backup=$(cat "$LAST_FULL_BACKUP_FILE")
local last_full_dir="$FULL_BACKUP_DIR/$last_full_backup"
local backup_date=$(date +%Y%m%d%H%M%S)
local target_dir="$INCREMENTAL_BACKUP_DIR/$backup_date"
if run_backup "$target_dir" "$last_full_dir"; then
echo "增量备份成功,备份目录: $target_dir"
else
echo "增量备份失败"
exit 1
fi
}
# 判断是否需要全量备份
if [ ! -f "$LAST_FULL_BACKUP_FILE" ]; then
full_backup
else
last_full_backup_date=$(cat "$LAST_FULL_BACKUP_FILE")
# 将日期字符串转换为 YYYY-MM-DD HH:MM:SS 格式
formatted_date=$(echo "$last_full_backup_date" | sed 's/\(....\)\(..\)\(..\)\(..\)\(..\)\(..\)/\1-\2-\3 \4:\5:\6/')
# 获取当前时间戳
current_timestamp=$(date +%s)
# 获取上次全量备份的时间戳
last_full_timestamp=$(date -d "$formatted_date" +%s)
if [ $? -eq 0 ]; then
days_since_last_full=$(( (current_timestamp - last_full_timestamp) / 86400 ))
if [ $days_since_last_full -ge $FULL_BACKUP_INTERVAL ]; then
full_backup
else
incremental_backup
fi
else
echo "日期解析失败,执行全量备份"
full_backup
fi
fi