448 lines
14 KiB
Bash
448 lines
14 KiB
Bash
#!/bin/bash
|
|
# ============================================================================
|
|
# MySQL 8.0.24 全量备份脚本
|
|
# ============================================================================
|
|
# 作者: AI Assistant
|
|
# 版本: 1.0.0
|
|
# 说明: 使用 mysqldump 进行全量逻辑备份
|
|
#
|
|
# 功能特点:
|
|
# - 支持单数据库或所有数据库备份
|
|
# - 记录 binlog 位置用于后续增量备份
|
|
# - 支持压缩备份
|
|
# - 自动清理过期备份
|
|
# - 完善的错误处理和日志记录
|
|
#
|
|
# 使用方法:
|
|
# ./full_backup.sh [选项]
|
|
#
|
|
# 选项:
|
|
# -d, --database <name> 指定要备份的数据库 (可多次使用)
|
|
# -c, --compress 启用压缩 (默认: 是)
|
|
# -n, --no-compress 禁用压缩
|
|
# -h, --help 显示帮助信息
|
|
#
|
|
# 示例:
|
|
# ./full_backup.sh # 备份所有数据库
|
|
# ./full_backup.sh -d mydb # 仅备份 mydb 数据库
|
|
# ./full_backup.sh -d db1 -d db2 # 备份多个指定数据库
|
|
# ============================================================================
|
|
|
|
set -o pipefail # 管道命令中任何一个失败都会导致整个管道失败
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# 脚本路径和配置加载
|
|
# ----------------------------------------------------------------------------
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
|
|
# 加载配置文件
|
|
if [[ ! -f "${SCRIPT_DIR}/config.sh" ]]; then
|
|
echo "[ERROR] 配置文件不存在: ${SCRIPT_DIR}/config.sh"
|
|
exit 1
|
|
fi
|
|
source "${SCRIPT_DIR}/config.sh"
|
|
|
|
# 加载公共函数库
|
|
if [[ ! -f "${SCRIPT_DIR}/lib/common.sh" ]]; then
|
|
echo "[ERROR] 公共函数库不存在: ${SCRIPT_DIR}/lib/common.sh"
|
|
exit 1
|
|
fi
|
|
source "${SCRIPT_DIR}/lib/common.sh"
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# 全局变量
|
|
# ----------------------------------------------------------------------------
|
|
BACKUP_TIMESTAMP=$(date +"${TIMESTAMP_FORMAT}")
|
|
BACKUP_NAME="${BACKUP_PREFIX}_full_${BACKUP_TIMESTAMP}"
|
|
BACKUP_DIR="${FULL_BACKUP_DIR}/${BACKUP_NAME}"
|
|
LOCK_FILE="${BACKUP_ROOT_DIR}/.full_backup.lock"
|
|
LOG_FILE="${LOG_DIR}/full_backup_${BACKUP_TIMESTAMP}.log"
|
|
ENABLE_COMPRESS=true
|
|
SPECIFIED_DATABASES=()
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# 显示帮助信息
|
|
# ----------------------------------------------------------------------------
|
|
show_help() {
|
|
cat << EOF
|
|
MySQL 8.0.24 全量备份脚本
|
|
|
|
使用方法:
|
|
$(basename "$0") [选项]
|
|
|
|
选项:
|
|
-d, --database <name> 指定要备份的数据库 (可多次使用)
|
|
-c, --compress 启用压缩 (默认: 是)
|
|
-n, --no-compress 禁用压缩
|
|
-h, --help 显示此帮助信息
|
|
|
|
示例:
|
|
$(basename "$0") # 备份所有数据库
|
|
$(basename "$0") -d mydb # 仅备份 mydb 数据库
|
|
$(basename "$0") -d db1 -d db2 # 备份多个指定数据库
|
|
$(basename "$0") --no-compress # 不压缩备份
|
|
|
|
环境要求:
|
|
- MySQL 8.0.24
|
|
- mysqldump 工具
|
|
- 足够的磁盘空间
|
|
- MySQL 用户需要 RELOAD, LOCK TABLES, REPLICATION CLIENT 权限
|
|
EOF
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# 解析命令行参数
|
|
# ----------------------------------------------------------------------------
|
|
parse_args() {
|
|
while [[ $# -gt 0 ]]; do
|
|
case "$1" in
|
|
-d|--database)
|
|
shift
|
|
if [[ -z "$1" ]]; then
|
|
die "选项 -d/--database 需要一个参数"
|
|
fi
|
|
SPECIFIED_DATABASES+=("$1")
|
|
shift
|
|
;;
|
|
-c|--compress)
|
|
ENABLE_COMPRESS=true
|
|
shift
|
|
;;
|
|
-n|--no-compress)
|
|
ENABLE_COMPRESS=false
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
show_help
|
|
exit 0
|
|
;;
|
|
*)
|
|
die "未知选项: $1\n使用 --help 查看帮助"
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# 初始化备份环境
|
|
# ----------------------------------------------------------------------------
|
|
init_backup() {
|
|
log_info "============================================================"
|
|
log_info "MySQL 全量备份开始"
|
|
log_info "============================================================"
|
|
log_info "备份时间: $BACKUP_TIMESTAMP"
|
|
log_info "备份目录: $BACKUP_DIR"
|
|
log_info "日志文件: $LOG_FILE"
|
|
log_info "============================================================"
|
|
|
|
# 设置错误处理
|
|
setup_error_trap
|
|
|
|
# 检查必要命令
|
|
check_commands "$MYSQLDUMP_PATH" "$MYSQL_PATH" "gzip"
|
|
|
|
# 检查 MySQL 连接
|
|
check_mysql_connection
|
|
|
|
# 创建必要目录
|
|
ensure_dir "$BACKUP_DIR"
|
|
ensure_dir "$LOG_DIR"
|
|
|
|
# 检查磁盘空间 (预估需要 5GB)
|
|
check_disk_space "$BACKUP_DIR" 5120
|
|
|
|
# 获取锁
|
|
acquire_lock "$LOCK_FILE" "全量备份"
|
|
|
|
# 设置清理 trap
|
|
trap cleanup EXIT
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# 清理函数
|
|
# ----------------------------------------------------------------------------
|
|
cleanup() {
|
|
local exit_code=$?
|
|
|
|
# 释放锁
|
|
release_lock "$LOCK_FILE"
|
|
|
|
if [[ $exit_code -ne 0 ]]; then
|
|
log_error "备份过程中发生错误,退出码: $exit_code"
|
|
|
|
# 清理不完整的备份
|
|
if [[ -d "$BACKUP_DIR" ]]; then
|
|
log_warn "清理不完整的备份目录: $BACKUP_DIR"
|
|
rm -rf "$BACKUP_DIR"
|
|
fi
|
|
|
|
send_notification "[失败] MySQL 全量备份" "备份失败,请检查日志: $LOG_FILE"
|
|
fi
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# 获取要备份的数据库列表
|
|
# ----------------------------------------------------------------------------
|
|
get_backup_databases() {
|
|
if [[ ${#SPECIFIED_DATABASES[@]} -gt 0 ]]; then
|
|
# 使用命令行指定的数据库
|
|
echo "${SPECIFIED_DATABASES[@]}"
|
|
elif [[ -n "$DATABASES_TO_BACKUP" ]]; then
|
|
# 使用配置文件中指定的数据库
|
|
echo "$DATABASES_TO_BACKUP"
|
|
else
|
|
# 备份所有数据库 (排除系统数据库)
|
|
get_databases
|
|
fi
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# 执行数据库备份
|
|
# ----------------------------------------------------------------------------
|
|
backup_database() {
|
|
local db_name="$1"
|
|
local output_file="${BACKUP_DIR}/${db_name}.sql"
|
|
|
|
log_info "备份数据库: $db_name"
|
|
|
|
# 构建 mysqldump 命令
|
|
# --single-transaction: 使用事务保证一致性 (InnoDB)
|
|
# --master-data=2: 将 binlog 位置信息以注释形式写入
|
|
# --routines: 包含存储过程和函数
|
|
# --triggers: 包含触发器
|
|
# --events: 包含事件调度器
|
|
# --set-gtid-purged=OFF: 避免 GTID 相关问题
|
|
local dump_cmd="$MYSQLDUMP_PATH"
|
|
local dump_args="-h${MYSQL_HOST} -P${MYSQL_PORT} -u${MYSQL_USER}"
|
|
|
|
if [[ -n "$MYSQL_PASSWORD" ]]; then
|
|
dump_args="$dump_args -p${MYSQL_PASSWORD}"
|
|
fi
|
|
|
|
dump_args="$dump_args --single-transaction --master-data=2"
|
|
dump_args="$dump_args --routines --triggers --events"
|
|
dump_args="$dump_args --set-gtid-purged=OFF"
|
|
dump_args="$dump_args --hex-blob" # 二进制数据使用十六进制格式
|
|
dump_args="$dump_args --quick" # 不缓存查询结果,节省内存
|
|
dump_args="$dump_args --lock-tables=false" # 使用 single-transaction 时不需要锁表
|
|
|
|
# 执行备份
|
|
log_debug "执行命令: $dump_cmd $dump_args $db_name"
|
|
|
|
if ! $dump_cmd $dump_args "$db_name" > "$output_file" 2>> "$LOG_FILE"; then
|
|
die "数据库 $db_name 备份失败"
|
|
fi
|
|
|
|
# 压缩备份文件
|
|
if [[ "$ENABLE_COMPRESS" == "true" ]]; then
|
|
log_info "压缩备份文件: ${output_file}"
|
|
gzip -f "$output_file" || die "压缩失败: $output_file"
|
|
output_file="${output_file}.gz"
|
|
fi
|
|
|
|
local file_size=$(get_file_size "$output_file")
|
|
log_info "数据库 $db_name 备份完成,大小: $file_size"
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# 备份所有数据库 (使用 --all-databases)
|
|
# ----------------------------------------------------------------------------
|
|
backup_all_databases() {
|
|
local output_file="${BACKUP_DIR}/all_databases.sql"
|
|
|
|
log_info "备份所有数据库"
|
|
|
|
local dump_cmd="$MYSQLDUMP_PATH"
|
|
local dump_args="-h${MYSQL_HOST} -P${MYSQL_PORT} -u${MYSQL_USER}"
|
|
|
|
if [[ -n "$MYSQL_PASSWORD" ]]; then
|
|
dump_args="$dump_args -p${MYSQL_PASSWORD}"
|
|
fi
|
|
|
|
dump_args="$dump_args --all-databases"
|
|
dump_args="$dump_args --single-transaction --master-data=2"
|
|
dump_args="$dump_args --routines --triggers --events"
|
|
dump_args="$dump_args --set-gtid-purged=OFF"
|
|
dump_args="$dump_args --hex-blob --quick"
|
|
dump_args="$dump_args --lock-tables=false"
|
|
|
|
log_debug "执行命令: $dump_cmd $dump_args"
|
|
|
|
if ! $dump_cmd $dump_args > "$output_file" 2>> "$LOG_FILE"; then
|
|
die "全库备份失败"
|
|
fi
|
|
|
|
# 压缩备份文件
|
|
if [[ "$ENABLE_COMPRESS" == "true" ]]; then
|
|
log_info "压缩备份文件: ${output_file}"
|
|
gzip -f "$output_file" || die "压缩失败: $output_file"
|
|
output_file="${output_file}.gz"
|
|
fi
|
|
|
|
local file_size=$(get_file_size "$output_file")
|
|
log_info "全库备份完成,大小: $file_size"
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# 记录 binlog 位置信息
|
|
# ----------------------------------------------------------------------------
|
|
save_binlog_position() {
|
|
local binlog_info_file="${BACKUP_DIR}/binlog_position.txt"
|
|
|
|
log_info "记录 binlog 位置信息"
|
|
|
|
local binlog_position=$(get_binlog_position)
|
|
local binlog_file=$(echo "$binlog_position" | cut -d: -f1)
|
|
local binlog_pos=$(echo "$binlog_position" | cut -d: -f2)
|
|
|
|
cat > "$binlog_info_file" << EOF
|
|
# MySQL Binlog Position
|
|
# 备份时间: $BACKUP_TIMESTAMP
|
|
# 用于增量备份的起始位置
|
|
|
|
BINLOG_FILE=$binlog_file
|
|
BINLOG_POSITION=$binlog_pos
|
|
EOF
|
|
|
|
log_info "Binlog 位置: $binlog_file:$binlog_pos"
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# 保存备份元数据
|
|
# ----------------------------------------------------------------------------
|
|
save_metadata() {
|
|
local metadata_file="${BACKUP_DIR}/metadata.txt"
|
|
|
|
log_info "保存备份元数据"
|
|
|
|
local end_time=$(date +%s)
|
|
local duration=$(calculate_duration "$START_TIME" "$end_time")
|
|
local backup_size=$(get_file_size "$BACKUP_DIR")
|
|
|
|
cat > "$metadata_file" << EOF
|
|
# MySQL 全量备份元数据
|
|
# ========================================
|
|
|
|
备份类型: 全量备份 (Full Backup)
|
|
备份时间: $BACKUP_TIMESTAMP
|
|
备份目录: $BACKUP_NAME
|
|
备份大小: $backup_size
|
|
执行耗时: $duration
|
|
|
|
MySQL 服务器: ${MYSQL_HOST}:${MYSQL_PORT}
|
|
MySQL 用户: $MYSQL_USER
|
|
|
|
备份选项:
|
|
- 压缩: $ENABLE_COMPRESS
|
|
- 压缩工具: $COMPRESS_TOOL
|
|
|
|
备份的数据库:
|
|
$(get_backup_databases | tr ' ' '\n' | sed 's/^/- /')
|
|
|
|
备份状态: 成功
|
|
EOF
|
|
|
|
log_info "元数据已保存"
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# 验证备份完整性
|
|
# ----------------------------------------------------------------------------
|
|
verify_backup() {
|
|
log_info "验证备份完整性"
|
|
|
|
local backup_files=$(find "$BACKUP_DIR" -name "*.sql*" -type f)
|
|
|
|
if [[ -z "$backup_files" ]]; then
|
|
die "备份验证失败: 没有找到备份文件"
|
|
fi
|
|
|
|
# 检查每个备份文件
|
|
while IFS= read -r file; do
|
|
local file_size=$(stat -f%z "$file" 2>/dev/null || stat -c%s "$file" 2>/dev/null)
|
|
|
|
if [[ $file_size -eq 0 ]]; then
|
|
die "备份验证失败: 文件为空 - $file"
|
|
fi
|
|
|
|
# 如果是压缩文件,验证压缩完整性
|
|
if [[ "$file" == *.gz ]]; then
|
|
if ! gzip -t "$file" 2>/dev/null; then
|
|
die "备份验证失败: 压缩文件损坏 - $file"
|
|
fi
|
|
fi
|
|
|
|
log_debug "文件验证通过: $file ($(get_file_size $file))"
|
|
done <<< "$backup_files"
|
|
|
|
log_info "备份验证通过"
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# 清理过期备份
|
|
# ----------------------------------------------------------------------------
|
|
cleanup_expired_backups() {
|
|
log_info "检查过期备份..."
|
|
cleanup_old_backups "$FULL_BACKUP_DIR" "$FULL_BACKUP_RETENTION_DAYS"
|
|
}
|
|
|
|
# ----------------------------------------------------------------------------
|
|
# 主函数
|
|
# ----------------------------------------------------------------------------
|
|
main() {
|
|
# 记录开始时间
|
|
START_TIME=$(date +%s)
|
|
|
|
# 解析参数
|
|
parse_args "$@"
|
|
|
|
# 初始化
|
|
init_backup
|
|
|
|
# 记录 binlog 位置 (在备份开始时)
|
|
save_binlog_position
|
|
|
|
# 获取要备份的数据库
|
|
local databases=$(get_backup_databases)
|
|
|
|
if [[ ${#SPECIFIED_DATABASES[@]} -gt 0 ]] || [[ -n "$DATABASES_TO_BACKUP" ]]; then
|
|
# 分别备份每个数据库
|
|
for db in $databases; do
|
|
backup_database "$db"
|
|
done
|
|
else
|
|
# 备份所有数据库
|
|
backup_all_databases
|
|
fi
|
|
|
|
# 验证备份
|
|
verify_backup
|
|
|
|
# 保存元数据
|
|
save_metadata
|
|
|
|
# 清理过期备份
|
|
cleanup_expired_backups
|
|
|
|
# 计算总耗时
|
|
local end_time=$(date +%s)
|
|
local duration=$(calculate_duration "$START_TIME" "$end_time")
|
|
local backup_size=$(get_file_size "$BACKUP_DIR")
|
|
|
|
log_info "============================================================"
|
|
log_info "MySQL 全量备份完成"
|
|
log_info "============================================================"
|
|
log_info "备份目录: $BACKUP_DIR"
|
|
log_info "备份大小: $backup_size"
|
|
log_info "执行耗时: $duration"
|
|
log_info "============================================================"
|
|
|
|
# 发送成功通知
|
|
send_notification "[成功] MySQL 全量备份" "备份完成\n目录: $BACKUP_DIR\n大小: $backup_size\n耗时: $duration"
|
|
|
|
return 0
|
|
}
|
|
|
|
# 执行主函数
|
|
main "$@"
|