本文提供了一个专为数据库/中间件 MySQL 8.0设计的逻辑备份Shell脚本,它使用MySQL Shell的util.dumpInstance工具执行实例级全量备份,是替代传统mysqldump的现代化方案。脚本集成了错误处理、日志记录、空间检查和自动清理等生产级功能。
脚本功能
此脚本用于MySQL 8.0的逻辑备份,通过MySQL Shell备份数据库实例的所有对象,实现实例级别的完整备份。
脚本内容
脚本名称为 mysqlshell_full_backup.sh
#!/bin/bash
#########################################
# MySQL Shell 自动备份脚本 (MySQL 8.0+)
# 功能: 全量逻辑备份 + 错误处理 + 日志记录 + 自动清理 + 时间统计
# 备份文件名格式: full_mysqlshell_backup_时间
# 使用方式: 直接修改脚本中的变量值,然后执行 ./mysqlshell_full_backup.sh
########################################
########################### 配置参数 ####################
# MySQL Shell命令绝对路径(重要:请根据实际安装路径修改)
MYSQLSH_CMD="/data/soft/mysqlshell8044/bin/mysqlsh"
# 备份保留天数(默认15天)
RETAIN_DAYS=${RETAIN_DAYS:-15}
# 并行度(默认4线程)
PARALLEL=${PARALLEL:-4}
# 排除的数据库(逗号分隔,默认空)
EXCLUDE_SCHEMAS=${EXCLUDE_SCHEMAS:-""}
# 备份存储根目录
BACKUP_ROOT="/data/backup"
# 时间戳格式:YYYYMMDD_HHMMSS
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
# 备份目录名称(按您要求的格式)
BACKUP_DIR_NAME="full_mysqlshell_backup_${TIMESTAMP}"
BACKUP_DIR="${BACKUP_ROOT}/${BACKUP_DIR_NAME}"
# MySQL连接参数(请根据实际情况修改)
MYSQL_USER="root"
MYSQL_PASSWORD="mysql"
MYSQL_HOST="localhost"
MYSQL_PORT="3306"
# 日志文件路径
LOG_FILE="${BACKUP_ROOT}/backup.log"
######################################################
########## 函数:记录日志 ##########
log() {
local level="$1"
local message="$2"
local timestamp=$(date '+%Y-%m-%d %H:%M:%S')
local log_entry="[${timestamp}] [${level}] ${message}"
# 输出到标准输出并写入日志文件
echo "${log_entry}" | tee -a "${LOG_FILE}"
}
########## 函数:错误处理并退出 ##########
error_exit() {
log "ERROR" "$1"
end_time=$(date +%s)
total_runtime=$((end_time - start_time_global))
log "INFO" "脚本异常退出,总运行时间: $(format_duration $total_runtime)"
exit 1
}
########## 函数:计算和格式化运行时间 ##########
format_duration() {
local seconds=$1
local hours=$((seconds / 3600))
local minutes=$(( (seconds % 3600) / 60 ))
local secs=$((seconds % 60))
if [ $hours -gt 0 ]; then
echo "${hours}小时${minutes}分${secs}秒"
elif [ $minutes -gt 0 ]; then
echo "${minutes}分${secs}秒"
else
echo "${secs}秒"
fi
}
########## 函数:检查依赖工具 ##########
check_dependencies() {
# 检查MySQL Shell是否存在且可执行
if [ ! -x "$MYSQLSH_CMD" ]; then
error_exit "MySQL Shell未在指定路径找到或不可执行: $MYSQLSH_CMD"
fi
# 检查其他依赖工具
local deps=("du")
for dep in "${deps[@]}"; do
if ! command -v "$dep" &> /dev/null; then
error_exit "所需工具 $dep 未安装或未在PATH中"
fi
done
log "INFO" "依赖检查通过,MySQL Shell路径: $MYSQLSH_CMD"
}
########## 函数:检查MySQL连接 ##########
check_mysql_connection() {
log "INFO" "检查MySQL数据库连接..."
if "$MYSQLSH_CMD" --log-level=2 --uri="${MYSQL_USER}:${MYSQL_PASSWORD}@${MYSQL_HOST}:${MYSQL_PORT}" --sql --execute "select 1" >/dev/null 2>&1; then
log "INFO" "MySQL数据库连接成功"
else
error_exit "MySQL数据库连接失败,请检查连接参数"
fi
}
########## 函数:检查磁盘空间 ##########
check_disk_space() {
local available_space=$(df "$BACKUP_ROOT" | awk 'NR==2 {print $4}')
local min_space=$((1024 * 1024)) # 至少保留1GB空间
if [ "$available_space" -lt "$min_space" ]; then
error_exit "磁盘空间不足,剩余空间: ${available_space}KB,至少需要: ${min_space}KB"
fi
log "INFO" "磁盘空间检查通过,剩余空间: ${available_space}KB"
}
########## 主函数:执行备份 ##########
perform_backup() {
log "INFO" "开始执行数据库备份..."
# 构建excludeSchemas参数
local exclude_param=""
if [ -n "$EXCLUDE_SCHEMAS" ]; then
local exclude_jsons=()
IFS=',' read -ra DB_ARRAY <<< "$EXCLUDE_SCHEMAS"
for db in "${DB_ARRAY[@]}"; do
db_clean=$(echo "$db" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' -e "s/'/\\\\'/g")
if [ -n "$db_clean" ]; then
exclude_jsons+=("'$db_clean'")
fi
done
if [ ${#exclude_jsons[@]} -gt 0 ]; then
exclude_param="excludeSchemas: [$(IFS=,; echo "${exclude_jsons}")],"
log "INFO" "排除数据库: ${EXCLUDE_SCHEMAS}"
fi
fi
# 构建备份命令
local backup_cmd="util.dumpInstance('$BACKUP_DIR', {
threads: $PARALLEL,
${exclude_param}
consistent: true,
compression: 'zstd',
ocimds: true,
compatibility: ['strip_restricted_grants','strip_definers','strip_tablespaces','ignore_missing_pks']
})"
log "INFO" "执行MySQL Shell备份命令,并行度: $PARALLEL"
# 执行备份(使用变量化的MYSQLSH_CMD)
if "$MYSQLSH_CMD" --log-level=3 --uri="${MYSQL_USER}:${MYSQL_PASSWORD}@${MYSQL_HOST}:${MYSQL_PORT}" \
--execute "$backup_cmd" >> "$LOG_FILE" 2>&1; then
log "INFO" "MySQL Shell备份命令执行完成"
return 0
else
return 1
fi
}
########## 主函数:清理过期备份 ##########
cleanup_old_backups() {
log "INFO" "开始清理超过 ${RETAIN_DAYS} 天的旧备份..."
local deleted_count=0
while IFS= read -r -d '' old_backup; do
if [ -n "$old_backup" ] && [ "$old_backup" != "$BACKUP_ROOT" ]; then
log "INFO" "删除过期备份: $(basename "$old_backup")"
rm -rf "$old_backup"
((deleted_count++))
fi
done < <(find "$BACKUP_ROOT" -maxdepth 1 -type d -name "full_mysqlshell_backup_*" -mtime "+$((RETAIN_DAYS-1))" -print0 2>/dev/null)
log "INFO" "清理完成,共删除 $deleted_count 个过期备份"
}
########## 主执行流程 ##########
main() {
local start_time_global=$(date +%s)
log "INFO" "=== MySQL备份作业开始 ==="
log "INFO" "备份目录: $BACKUP_DIR"
log "INFO" "保留天数: $RETAIN_DAYS天, 并行度: $PARALLEL"
log "INFO" "排除数据库: ${EXCLUDE_SCHEMAS:-无}"
log "INFO" "MySQL Shell路径: $MYSQLSH_CMD"
# 初始化检查
check_dependencies
check_disk_space
check_mysql_connection
# 创建备份目录
if ! mkdir -p "$BACKUP_DIR"; then
error_exit "无法创建备份目录: $BACKUP_DIR"
fi
log "INFO" "备份目录创建成功: $BACKUP_DIR"
# 执行备份
local backup_start=$(date +%s)
if perform_backup; then
local backup_end=$(date +%s)
local backup_runtime=$((backup_end - backup_start))
log "INFO" "数据库备份成功完成"
log "INFO" "备份耗时: $(format_duration $backup_runtime)"
else
error_exit "数据库备份执行失败,详情请查看日志: $LOG_FILE"
fi
# 清理过期备份
local cleanup_start=$(date +%s)
cleanup_old_backups
local cleanup_end=$(date +%s)
local cleanup_runtime=$((cleanup_end - cleanup_start))
# 最终统计
local end_time_global=$(date +%s)
local total_runtime=$((end_time_global - start_time_global))
log "INFO" "=== 备份作业统计 ==="
log "INFO" "备份文件位置: $BACKUP_DIR"
log "INFO" "备份大小: $(du -sh "$BACKUP_DIR" 2>/dev/null | cut -f1 || echo "未知")"
log "INFO" "各阶段耗时详情:"
log "INFO" " - 备份阶段: $(format_duration $backup_runtime)"
log "INFO" " - 清理阶段: $(format_duration $cleanup_runtime)"
log "INFO" " - 总计耗时: $(format_duration $total_runtime)"
log "INFO" "日志文件: $LOG_FILE"
log "INFO" "=== MySQL备份作业完成 ==="
}
########## 脚本执行入口 - 直接执行主函数 ##########
main
关键参数说明
备份命令中的核心参数构建如下:
local backup_cmd="util.dumpInstance('$BACKUP_DIR', {
threads: $PARALLEL,
${exclude_param}
consistent: true,
compression: 'zstd',
ocimds: true,
compatibility: ['strip_restricted_grants','strip_definers','strip_tablespaces','ignore_missing_pks']
})"
其中,compatibility 兼容性参数设置需要特别注意:
- strip_restricted_grants:移除目标数据库服务不允许授予的高级权限,避免因权限问题导致导入失败。
- strip_definers:从视图、存储过程等对象定义中移除
DEFINER= 子句,避免因原始定义者不存在而导致的权限错误。
- strip_tablespaces:从建表语句中移除
TABLESPACE= 子句,防止因指定不存在的表空间而导致建表失败。
- ignore_missing_pks:忽略(跳过检查)没有主键的表,而不为它们自动创建主键,用于容忍没有主键的表,但不会自动修复。如需自动添加主键,应使用
create_invisible_pks 选项。
准备工作
MySQL数据库软件本身不包含MySQL Shell,需要单独下载安装。您可以从 MySQL官网 下载对应版本,解压后即可使用。

使用方法
1. 保存脚本并赋予执行权限
chmod +x mysqlshell_full_backup.sh
2. 手动执行备份
./mysqlshell_full_backup.sh

执行后可以查看日志:
tail -100 /data/backup/backup.log

3. 配置定时任务(例如每日凌晨1点执行)
编辑crontab (crontab -e),添加以下内容:
0 1 * * * /path/to/mysqlshell_full_backup.sh
数据恢复指南
使用MySQL Shell进行的备份,恢复时也必须使用MySQL Shell工具。
1. 确认备份文件完整性
恢复前,请检查备份目录是否包含 @.done.json 文件及各数据库的 .tsv.zst、.sql 等文件。
ls -l full_mysqlshell_backup_20251113_111713
输出示例如下:
total 23624
-rw-r----- 1 root root 549 Nov 13 11:17 @.done.json
-rw-r----- 1 root root 1400 Nov 13 11:17 @.json
...(其他文件列表)
2. 准备恢复环境
3. 执行恢复操作
恢复方式一(交互式):
mysqlsh -u root -p --host localhost --port 3306 --js
进入MySQL Shell的JavaScript模式后,执行:
util.loadDump("/data/backup/full_mysqlshell_backup_20251113_103022", {
threads: 4, // 并行线程数,可调整
skipBinlog: false, // 如果恢复过程不想记录到二进制日志,可设为true
ignoreVersion: true, // 忽略MySQL版本差异警告(谨慎使用)
resetProgress: true // 如果重新开始一个恢复,重置进度
});
恢复方式二(单行命令):
mysqlsh -u root -p -h localhost -P 3306 --js -e "util.loadDump('/data/backup/full_mysqlshell_backup_20251113_103022', {threads: 4, skipBinlog: false})"
4. 监控恢复过程
执行 util.loadDump 时会显示进度信息。您也可以在另一个会话中连接到MySQL,查看正在创建的对象。

总结
本脚本提供了一个用于生产环境的、完整的MySQL 8.0逻辑备份与恢复运维/DevOps解决方案。它基于MySQL Shell的 util.dumpInstance 工具,具备错误处理、日志记录、自动清理和耗时统计等实用功能。数据库管理员可根据实际环境灵活调整备份路径、保留天数及并行度等参数,并通过详细的恢复步骤确保数据可可靠回迁。