MySQL 数据库备份脚本使用说明

MySQL 数据库备份脚本使用说明

1. 脚本简介

本脚本用于备份 MySQL 或 MariaDB 数据库,支持:

  • 通过参数备份指定数据库
  • 通过参数全量备份所有数据库
  • 通过环境变量传递数据库地址、端口、用户名和密码
  • 自动使用 gzip 压缩备份文件
  • 自动检查压缩文件完整性
  • 自动生成 SHA-256 校验文件
  • 备份失败时自动清理未完成文件
  • 备份存储过程、函数、事件和触发器

脚本文件:mysql_backup.sh

2. 运行环境

执行服务器需要安装以下命令:

  • bash
  • mysqldump
  • gzip
  • mktemp

可以通过以下命令确认 mysqldump 是否已经安装:

mysqldump --version

3. 配置数据库连接信息

执行脚本前,先在当前终端中设置环境变量:

export DB_HOST="127.0.0.1"
export DB_PORT="3306"
export DB_USER="backup_user"
export DB_PASSWORD="your_password"

环境变量说明:

环境变量 是否必填 说明 默认值
DB_HOST 数据库服务器地址
DB_PORT 数据库端口 3306
DB_USER 数据库用户名
DB_PASSWORD 数据库密码
BACKUP_DIR 默认备份目录 ./backups

脚本执行结束后,如果不再需要这些变量,可以清除密码:

unset DB_PASSWORD

4. 添加执行权限

首次使用时执行:

chmod +x mysql_backup.sh

5. 备份指定数据库

./mysql_backup.sh --database your_database --output-dir /data/backups

简写方式:

./mysql_backup.sh -d your_database -o /data/backups

6. 全量备份

备份当前实例中的所有数据库:

./mysql_backup.sh --all --output-dir /data/backups

简写方式:

./mysql_backup.sh -a -o /data/backups

7. 使用默认备份目录

可以通过 BACKUP_DIR 设置默认目录:

export BACKUP_DIR="/data/backups"
./mysql_backup.sh --all

如果没有设置 BACKUP_DIR,也没有传入 --output-dir,备份文件将保存到当前目录下的 backups 文件夹。

8. 查看帮助

./mysql_backup.sh --help

9. 备份文件格式

指定数据库备份示例:

127.0.0.1_your_database_20260911_143000.sql.gz
127.0.0.1_your_database_20260911_143000.sql.gz.sha256

全量备份示例:

127.0.0.1_all_databases_20260911_143000.sql.gz
127.0.0.1_all_databases_20260911_143000.sql.gz.sha256

其中:

  • .sql.gz 是经过 gzip 压缩的数据库备份文件。
  • .sha256 是备份文件的 SHA-256 校验文件。

10. 校验备份文件

Linux 环境:

sha256sum -c 127.0.0.1_your_database_20260911_143000.sql.gz.sha256

也可以检查 gzip 文件是否损坏:

gzip -t 127.0.0.1_your_database_20260911_143000.sql.gz

命令没有输出且返回成功,表示压缩文件结构正常。

11. 恢复指定数据库备份

脚本使用 --databases 生成单库备份,备份内容包含创建和选择数据库的语句。恢复命令如下:

gzip -dc 127.0.0.1_your_database_20260911_143000.sql.gz | \
  mysql -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USER" -p

执行后,MySQL 客户端会提示输入密码。

12. 恢复全量备份

gzip -dc 127.0.0.1_all_databases_20260911_143000.sql.gz | \
  mysql -h "$DB_HOST" -P "$DB_PORT" -u "$DB_USER" -p

全量恢复可能覆盖同名数据库中的现有对象。执行前应确认恢复目标,并提前保留当前数据。

13. 安全说明

  • 数据库密码通过环境变量传入,不会作为 mysqldump 命令行参数出现。
  • 脚本会创建权限为 600 的临时 MySQL 配置文件,并在执行结束后删除。
  • 脚本使用 umask 077,新生成的备份文件默认仅允许当前系统用户访问。
  • 建议创建专用备份账号,不要直接使用 MySQL root 账号。
  • 不要把包含真实密码的 export 命令写入代码仓库或公共脚本。
  • 建议将备份文件复制到独立服务器或对象存储中,避免数据库服务器损坏时备份同时丢失。

14. 数据一致性说明

脚本默认使用 --single-transaction,适合以 InnoDB 为主的数据库,可以在不长时间锁表的情况下获得一致性备份。

如果数据库包含 MyISAM 等非事务表,--single-transaction 无法保证这些表在持续写入时的一致性。此类场景应选择业务低峰期备份、暂停写入,或者采用锁表和物理备份方案。

15. 建议的备份账号权限

实际权限需要根据 MySQL 版本、表结构和功能进行调整。常见逻辑备份账号可以参考:

CREATE USER 'backup_user'@'backup_server_ip' IDENTIFIED BY 'strong_password';
GRANT SELECT, SHOW VIEW, TRIGGER, EVENT ON *.* TO 'backup_user'@'backup_server_ip';
GRANT EXECUTE ON *.* TO 'backup_user'@'backup_server_ip';
FLUSH PRIVILEGES;

请将示例中的账号、来源地址和密码替换为实际值,并遵循最小权限原则。

16. 生产环境建议

  • 定期执行恢复演练;只有实际恢复成功的备份才是可信备份。
  • 对备份目录设置磁盘空间监控和告警。
  • 根据业务需求增加备份保留和过期清理策略。
  • 对重要备份进行异地保存和加密。
  • 记录每次备份的开始时间、结束时间、文件大小和执行结果。
  • 在数据量较大时,先评估逻辑备份对数据库负载、网络和磁盘空间的影响。

17. 完整脚本代码

将以下内容保存为 mysql_backup.sh,然后执行 chmod +x mysql_backup.sh 添加执行权限:

#!/usr/bin/env bash

# MySQL/MariaDB logical backup script.
# Credentials are read from DB_HOST, DB_PORT, DB_USER and DB_PASSWORD.

set -Eeuo pipefail
umask 077

PROGRAM_NAME="${0##*/}"
BACKUP_MODE=""
DATABASE_NAME=""
OUTPUT_DIR="${BACKUP_DIR:-./backups}"
TEMP_CNF=""
TEMP_BACKUP=""

usage() {
  cat <<EOF
Usage:
  ${PROGRAM_NAME} --database DATABASE [--output-dir DIRECTORY]
  ${PROGRAM_NAME} --all                 [--output-dir DIRECTORY]

Required environment variables:
  DB_HOST       MySQL server address
  DB_USER       MySQL user
  DB_PASSWORD   MySQL password

Optional environment variables:
  DB_PORT       MySQL port (default: 3306)
  BACKUP_DIR    Default output directory (default: ./backups)

Options:
  -d, --database DATABASE   Back up one database
  -a, --all                 Back up all databases
  -o, --output-dir DIR      Write backups to DIR
  -h, --help                Show this help
EOF
}

die() {
  printf 'ERROR: %s\n' "$*" >&2
  exit 1
}

log() {
  printf '[%s] %s\n' "$(date '+%Y-%m-%d %H:%M:%S')" "$*"
}

cleanup() {
  local status=$?
  [[ -z "$TEMP_CNF" ]] || rm -f -- "$TEMP_CNF"
  if (( status != 0 )) && [[ -n "$TEMP_BACKUP" ]]; then
    rm -f -- "$TEMP_BACKUP"
  fi
  exit "$status"
}
trap cleanup EXIT INT TERM

while (( $# > 0 )); do
  case "$1" in
    -d|--database)
      (( $# >= 2 )) || die "$1 requires a database name"
      [[ -z "$BACKUP_MODE" ]] || die "choose exactly one of --database or --all"
      BACKUP_MODE="database"
      DATABASE_NAME="$2"
      shift 2
      ;;
    -a|--all)
      [[ -z "$BACKUP_MODE" ]] || die "choose exactly one of --database or --all"
      BACKUP_MODE="all"
      shift
      ;;
    -o|--output-dir)
      (( $# >= 2 )) || die "$1 requires a directory"
      OUTPUT_DIR="$2"
      shift 2
      ;;
    -h|--help)
      usage
      exit 0
      ;;
    *)
      die "unknown argument: $1 (use --help for usage)"
      ;;
  esac
done

[[ -n "$BACKUP_MODE" ]] || die "choose --database DATABASE or --all"
[[ -n "${DB_HOST:-}" ]] || die "DB_HOST is not set"
[[ -n "${DB_USER:-}" ]] || die "DB_USER is not set"
[[ "${DB_PASSWORD+x}" == x ]] || die "DB_PASSWORD is not set"
DB_PORT="${DB_PORT:-3306}"
[[ "$DB_PORT" =~ ^[0-9]+$ ]] && (( DB_PORT >= 1 && DB_PORT <= 65535 )) || die "DB_PORT must be between 1 and 65535"

if [[ "$BACKUP_MODE" == "database" ]]; then
  [[ -n "$DATABASE_NAME" ]] || die "database name cannot be empty"
  [[ "$DATABASE_NAME" != -* && "$DATABASE_NAME" != *$'\n'* && "$DATABASE_NAME" != *$'\r'* ]] || die "invalid database name"
fi

for command_name in mysqldump gzip mktemp; do
  command -v "$command_name" >/dev/null 2>&1 || die "required command not found: $command_name"
done

mkdir -p -- "$OUTPUT_DIR"
[[ -d "$OUTPUT_DIR" && -w "$OUTPUT_DIR" ]] || die "output directory is not writable: $OUTPUT_DIR"

# Escape values for a MySQL option file, which prevents the password from
# appearing in the process list or command-line history.
cnf_escape() {
  local value="$1"
  value=${value//\\/\\\\}
  value=${value//\"/\\\"}
  printf '%s' "$value"
}

TEMP_CNF="$(mktemp "${TMPDIR:-/tmp}/mysql-backup.cnf.XXXXXX")"
chmod 600 "$TEMP_CNF"
{
  printf '[client]\n'
  printf 'host="%s"\n' "$(cnf_escape "$DB_HOST")"
  printf 'port=%s\n' "$DB_PORT"
  printf 'user="%s"\n' "$(cnf_escape "$DB_USER")"
  printf 'password="%s"\n' "$(cnf_escape "$DB_PASSWORD")"
} >"$TEMP_CNF"

timestamp="$(date '+%Y%m%d_%H%M%S')"
safe_host="${DB_HOST//[^[:alnum:]._-]/_}"
if [[ "$BACKUP_MODE" == "all" ]]; then
  backup_label="all_databases"
  dump_scope=(--all-databases)
else
  backup_label="${DATABASE_NAME//[^[:alnum:]._-]/_}"
  dump_scope=(--databases "$DATABASE_NAME")
fi

final_backup="${OUTPUT_DIR%/}/${safe_host}_${backup_label}_${timestamp}.sql.gz"
TEMP_BACKUP="$(mktemp "${OUTPUT_DIR%/}/.${safe_host}_${backup_label}_${timestamp}.XXXXXX.sql.gz")"

dump_options=(
  --defaults-extra-file="$TEMP_CNF"
  --single-transaction
  --quick
  --routines
  --events
  --triggers
  --hex-blob
  --default-character-set=utf8mb4
)

log "starting backup: ${backup_label} from ${DB_HOST}:${DB_PORT}"
if ! mysqldump "${dump_options[@]}" "${dump_scope[@]}" | gzip -c >"$TEMP_BACKUP"; then
  die "backup failed"
fi

[[ -s "$TEMP_BACKUP" ]] || die "backup output is empty"
gzip -t "$TEMP_BACKUP" || die "compressed backup verification failed"
mv -- "$TEMP_BACKUP" "$final_backup"
TEMP_BACKUP=""

if command -v sha256sum >/dev/null 2>&1; then
  sha256sum "$final_backup" >"${final_backup}.sha256"
elif command -v shasum >/dev/null 2>&1; then
  shasum -a 256 "$final_backup" >"${final_backup}.sha256"
fi

backup_size="$(du -h "$final_backup" | awk '{print $1}')"
log "backup completed: ${final_backup} (${backup_size})"
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

友情链接更多精彩内容