0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

一文详解MySQL备份与恢复基础流程

马哥Linux运维 来源:马哥Linux运维 2026-04-21 11:43 次阅读
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

背景

数据备份是数据库运维的最后一道防线。无论系统设计多么健壮、人为操作多么谨慎,硬件故障、软件 BUG、人为误删都可能在毫无预兆的情况下发生。没有可用的备份,意味着业务数据面临永久丢失的风险。2024 年国内某云厂商因备份系统缺陷导致客户数据无法恢复的案例,至今仍是运维圈的反面教材。

MySQL 8.0 的备份体系相比 5.7 时代有了显著进步。MySQL Shell 8.0(原名 MySQL Shell)提供了全新的备份恢复工具集,xtrabackup 也已更新到 8.0.35 版本,支持 MySQL 8.0 的所有新特性。2026 年,主流备份方案已从单一的物理备份或逻辑备份演变为“分层备份策略”——日常增量备份 + 定期全量备份 + 异地容灾。

本文以 MySQL 8.0.39 为基准,系统讲解全量备份、增量备份的原理与实践,覆盖 mysqldump、xtrabackup、mydumper 三种主流工具,提供可直接落地的脚本和完整的恢复演练流程。

前置知识要求:了解 MySQL 存储引擎(InnoDB/MyISAM)、熟悉 Linux 基础命令、理解 binlog 原理。

1. 备份的重要性与风险评估

1.1 备份的分级与 RPO/RTO

在讨论备份方案之前,运维必须明确两个关键指标:

RPO(Recovery Point Objective):可接受的最大数据丢失量,通常以时间衡量。比如 RPO = 1小时,意味着最多允许丢失1小时的数据。

RTO(Recovery Time Objective):系统恢复所需的最大时间。比如 RTO = 4小时,意味着故障后4小时内必须恢复服务。

不同业务场景的 RPO/RTO 要求:

业务场景 RPO RTO
核心交易系统 < 5分钟 < 30分钟
用户数据(账户、订单) < 1小时 < 4小时
日志类数据 < 24小时 < 24小时
归档历史数据 < 1周 < 1周

1.2 常见数据丢失场景

# 人为误操作风险评估清单
# 场景1: 误删除表
# 风险等级: 极高
# 恢复难度: 高(需要从备份恢复 + binlog 补数据)

# 场景2: 恶意truncate表
# 风险等级: 极高
# 恢复难度: 高(binlog 必须包含原始数据)

# 场景3: DROP DATABASE
# 风险等级: 灾难级
# 恢复难度: 极高(整个数据库需要重建)

# 场景4: 升级失败导致数据目录损坏
# 风险等级: 高
# 恢复难度: 中(如果有物理备份,恢复较快)

# 场景5: 主从切换后从库数据不一致
# 风险等级: 中
# 恢复难度: 低(重新从主库同步)

1.3 备份策略选择决策树

业务对数据丢失的容忍度?
  |
  +-- 极低(RPO < 5分钟)
    |   |
    |   +-- 方案: xtrabackup增量备份(每5-15分钟) + binlog实时备份
    |
    +-- 低(RPO 1小时以内)
    |   |
    |   +-- 方案: xtrabackup增量备份(每小时) + 每日全量
    |
    +-- 中等(RPO 1-24小时)
    |   |
    |   +-- 方案: 每日mysqldump全量 + binlog归档
    |
    +-- 宽松(RPO > 24小时)
    |
    +-- 方案: 每日mysqldump全量

2. 备份方法对比

2.1 逻辑备份 vs 物理备份

逻辑备份通过 SQL 语句导出数据,备份的是数据内容和表结构。工具包括mysqldump、mydumper。

优点:跨 MySQL 版本使用、备份文件可读、恢复灵活

缺点:备份速度慢、恢复速度更慢、无法保证数据一致性(备份过程中数据仍在写入)

物理备份直接复制 MySQL 数据文件,备份的是数据库的物理文件。工具包括xtrabackup、mysqlbackup。

优点:备份/恢复速度快、可以保持数据一致性(通过FTWRL或xtrabackup的内部机制)

缺点:备份文件大、只能恢复到相同版本的 MySQL、跨平台困难

2.2 主流工具对比

工具 类型 备份速度 恢复速度 数据一致性 增量备份 推荐场景
mysqldump 逻辑 需要--single-transaction 不支持 < 10GB,小型业务
mydumper 逻辑 支持事务 不支持 中大型数据库
xtrabackup 物理 支持(自动处理) 支持 大型数据库,核心业务
mysqlbackup 物理 支持 支持 Oracle MySQL 企业版

2.3 mysqldump 详解

mysqldump是 MySQL 自带的逻辑备份工具,几乎所有 MySQL 环境都可以直接使用。但它的使用有很多讲究,错误的用法会导致数据不一致或备份失败。

基础用法:

# 备份单个数据库
mysqldump -u root -p -h localhost mydb > /backup/mydb_$(date +%Y%m%d).sql

# 备份所有数据库
mysqldump -u root -p -h localhost --all-databases > /backup/all_$(date +%Y%m%d).sql

# 备份特定表
mysqldump -u root -p mydb users orders > /backup/mydb_tables_$(date +%Y%m%d).sql

# 压缩备份(节省空间)
mysqldump -u root -p mydb | gzip > /backup/mydb_$(date +%Y%m%d).sql.gz

一致性备份的关键参数:

# 使用事务保证 InnoDB 数据一致性(重要!)
mysqldump -u root -p 
  --single-transaction 
  --routines 
  --triggers 
  --events 
  --master-data=2 
  --flush-logs 
  mydb > /backup/mydb_$(date +%Y%m%d).sql

参数说明:

--single-transaction:对 InnoDB 表开启一个一致性快照事务,备份期间不影响业务读写

--routines:备份存储过程和函数

--triggers:备份触发器

--events:备份事件调度器事件

--master-data=2:在备份文件中记录 binlog 文件名和位置(用于从库搭建)

--flush-logs:备份前刷新日志,切割 binlog

警告:以下用法会导致数据不一致

# 错误示例:没有使用 --single-transaction
mysqldump -u root -p mydb > /backup/mydb.sql # MyISAM 表可能出现不一致

# 错误示例:混合使用 --lock-tables 和 --single-transaction
mysqldump -u root -p --lock-tables --single-transaction mydb # 两个参数互斥

2.4 xtrabackup 详解

xtrabackup 是 Percona 公司开发的物理备份工具,是 MySQL 大规模备份的首选方案。xtrabackup 8.0.35 支持 MySQL 8.0.39。

安装 xtrabackup:

#!/bin/bash
# install_xtrabackup.sh

# 方法1: 通过 Percona 仓库安装(推荐)
yum install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm
yum install -y percona-xtrabackup-80

# 方法2: 通过 APT 安装(Ubuntu/Debian)
wget -q https://repo.percona.com/apt/percona-release_latest.generic_all.deb
dpkg -i percona-release_latest.generic_all.deb
apt-get update
apt-get install -y percona-xtrabackup-80

全量备份:

#!/bin/bash
# xtrabackup_full_backup.sh
# MySQL 8.0 xtrabackup 全量备份脚本

BACKUP_DIR="/backup/xtrabackup"
MYSQL_USER="backup_user"
MYSQL_PASSWORD="BackupPass2026!"
MYSQL_SOCKET="/var/lib/mysql/mysql.sock"
RETENTION_DAYS=7

# 创建备份目录
mkdir -p"${BACKUP_DIR}/full"
mkdir -p"${BACKUP_DIR}/incr"

# 生成备份名称
BACKUP_NAME="full_$(date +%Y%m%d_%H%M%S)"
BACKUP_PATH="${BACKUP_DIR}/full/${BACKUP_NAME}"

echo"$(date '+%Y-%m-%d %H:%M:%S')- 开始全量备份..."

# 执行全量备份
xtrabackup 
  --user="${MYSQL_USER}"
  --password="${MYSQL_PASSWORD}"
  --socket="${MYSQL_SOCKET}"
  --backup 
  --target-dir="${BACKUP_PATH}"
  --datadir=/var/lib/mysql 
  --ftwrl-wait-timeout=300 
  --ftwrl-wait-threshold=200 
  --wait-for-mysql 
  --no-version-check 
  --parallel=4 
  --throttle=100

if[ $? -eq 0 ];then
 echo"$(date '+%Y-%m-%d %H:%M:%S')- 全量备份成功:${BACKUP_PATH}"

 # 准备备份(应用事务日志,使备份一致)
 echo"$(date '+%Y-%m-%d %H:%M:%S')- 开始准备备份..."
  xtrabackup 
    --prepare 
    --target-dir="${BACKUP_PATH}"
    --no-version-check

 echo"$(date '+%Y-%m-%d %H:%M:%S')- 备份准备完成"

 # 清理过期备份
  find"${BACKUP_DIR}/full"-typed -mtime +${RETENTION_DAYS}-execrm -rf {} ; 2>/dev/null
 echo"$(date '+%Y-%m-%d %H:%M:%S')- 已清理${RETENTION_DAYS}天前的备份"
else
 echo"$(date '+%Y-%m-%d %H:%M:%S')- 全量备份失败!"
 exit1
fi

增量备份:

#!/bin/bash
# xtrabackup_incr_backup.sh
# 基于上一次全量备份的增量备份脚本

BACKUP_DIR="/backup/xtrabackup"
MYSQL_USER="backup_user"
MYSQL_PASSWORD="BackupPass2026!"
MYSQL_SOCKET="/var/lib/mysql/mysql.sock"
BASE_BACKUP=""

# 获取最新的全量备份作为基准
BASE_BACKUP=$(ls -td${BACKUP_DIR}/full/full_* 2>/dev/null | head -1)

if[ -z"$BASE_BACKUP"];then
 echo"未找到全量备份,请先执行全量备份"
 exit1
fi

INCR_BACKUP="incr_$(date +%Y%m%d_%H%M%S)"
INCR_PATH="${BACKUP_DIR}/incr/${INCR_BACKUP}"

echo"$(date '+%Y-%m-%d %H:%M:%S')- 基于${BASE_BACKUP}执行增量备份..."

xtrabackup 
  --user="${MYSQL_USER}"
  --password="${MYSQL_PASSWORD}"
  --socket="${MYSQL_SOCKET}"
  --backup 
  --target-dir="${INCR_PATH}"
  --incremental-basedir="${BASE_BACKUP}"
  --datadir=/var/lib/mysql 
  --parallel=4

if[ $? -eq 0 ];then
 echo"$(date '+%Y-%m-%d %H:%M:%S')- 增量备份成功:${INCR_PATH}"
else
 echo"$(date '+%Y-%m-%d %H:%M:%S')- 增量备份失败!"
 exit1
fi

2.5 mydumper 详解

mydumper 是 mysqldump 的多线程替代品,备份速度通常比 mysqldump 快 3-10 倍,尤其适合大型数据库。

安装 mydumper:

# Ubuntu/Debian
apt-get install -y mydumper

# CentOS/RHEL
yum install -y mydumper

# 从源码编译
gitclonehttps://github.com/mydumper/mydumper.git
cdmydumper
cmake .
make -j$(nproc)
make install

基础用法:

# 备份单个数据库(多线程)
mydumper 
  -u root 
  -p'MyPassword2026!'
  -h localhost 
  -B mydb 
  -o /backup/mydb_$(date +%Y%m%d) 
  -t 8      # 8个线程
  -v 3      # verbose 模式

# 备份所有数据库
mydumper 
  -u root 
  -p'MyPassword2026!'
  -h localhost 
  -o /backup/all_$(date +%Y%m%d) 
  -t 8

恢复命令(myloader):

# 恢复数据库
myloader 
  -u root 
  -p'MyPassword2026!'
  -h localhost 
  -d /backup/mydb_20261015 
  -t 8 
  -B mydb

3. 全量备份脚本

3.1 定时全量备份脚本(支持加密压缩)

#!/bin/bash
# mysql_full_backup.sh
# MySQL 全量备份脚本 - 支持压缩、加密、异地传输
# 建议 cron: 0 2 * * * /opt/scripts/mysql_full_backup.sh

set-euo pipefail

# ==================== 配置区域 ====================
MYSQL_USER="backup_admin"
MYSQL_PASSWORD="SecureBackupPass2026!"
MYSQL_HOST="localhost"
MYSQL_SOCKET="/var/lib/mysql/mysql.sock"
BACKUP_ROOT="/backup/mysql"
RETENTION_DAYS=30
GZIP_LEVEL=6
ENCRYPT_KEY_FILE="/etc/backup/aes_keyfile"
REMOTE_BACKUP_HOST="backup-server.example.com"
REMOTE_BACKUP_USER="rsync_user"
REMOTE_BACKUP_PATH="/backup/mysql"
ENABLE_REMOTE_SYNC=false
ENABLE_ENCRYPT=false
# ==================== 配置结束 ====================

# 日志函数
log() {
 echo"[$(date '+%Y-%m-%d %H:%M:%S')]$1"
}

# 错误处理函数
error_exit() {
 log"ERROR:$1"
 exit1
}

# 检查 MySQL 连接
check_mysql() {
 log"检查 MySQL 连接..."
 if! mysql -u"${MYSQL_USER}"-p"${MYSQL_PASSWORD}"-h"${MYSQL_HOST}"-e"SELECT 1">/dev/null 2>&1;then
    error_exit"MySQL 连接失败"
 fi
 log"MySQL 连接正常"
}

# 获取备份数据库列表
get_databases() {
  mysql -u"${MYSQL_USER}"-p"${MYSQL_PASSWORD}"-h"${MYSQL_HOST}"-N -e"SHOW DATABASES"2>/dev/null | 
    grep -vE'(information_schema|performance_schema|mysql|sys|test)'
}

# 备份单个数据库
backup_database() {
 localdb_name=$1
 localbackup_file="${BACKUP_ROOT}/${BACKUP_DATE}/${db_name}.sql"

 log"备份数据库:${db_name}"

 # 使用 --single-transaction 保证 InnoDB 一致性
 # 使用 --master-data=2 记录 binlog 位置
  mysqldump 
    --user="${MYSQL_USER}"
    --password="${MYSQL_PASSWORD}"
    --host="${MYSQL_HOST}"
    --socket="${MYSQL_SOCKET}"
    --single-transaction 
    --routines 
    --triggers 
    --events 
    --master-data=2 
    --flush-logs 
    --lock-tables=false
    --add-drop-database 
    --databases"${db_name}"2>/dev/null | 
    gzip -${GZIP_LEVEL}>"${backup_file}.gz"

 if[ $? -eq 0 ] && [ -s"${backup_file}.gz"];then
   log"数据库${db_name}备份成功:$(du -h ${backup_file}.gz | cut -f1)"
 else
    error_exit"数据库${db_name}备份失败"
 fi
}

# 备份所有表结构(不含数据)
backup_schema() {
 localdb_name=$1
 localschema_file="${BACKUP_ROOT}/${BACKUP_DATE}/${db_name}_schema.sql"

  mysqldump 
    --user="${MYSQL_USER}"
    --password="${MYSQL_PASSWORD}"
    --host="${MYSQL_HOST}"
    --socket="${MYSQL_SOCKET}"
    --no-data 
    --routines 
    --triggers 
    --events 
    --databases"${db_name}"2>/dev/null | 
    gzip -${GZIP_LEVEL}>"${schema_file}.gz"

 log"数据库${db_name}表结构备份完成"
}

# 生成备份元信息
generate_metadata() {
 localmetadata_file="${BACKUP_ROOT}/${BACKUP_DATE}/metadata.txt"

  {
   echo"Backup Date:${BACKUP_DATE}"
   echo"Backup Time:$(date '+%Y-%m-%d %H:%M:%S')"
   echo"MySQL Version:$(mysql -u "${MYSQL_USER}" -p"${MYSQL_PASSWORD}" -h "${MYSQL_HOST}" -N -e "SELECT VERSION();"2>/dev/null)"
    echo "Binlog Position: $(mysql -u"${MYSQL_USER}"-p"${MYSQL_PASSWORD}"-h"${MYSQL_HOST}"-N -e"SHOW MASTER STATUS;"2>/dev/null | head -1)"
    echo "Databases: $(get_databases | tr'
'', ')"
  } > "${metadata_file}"

  log "元信息文件已生成"
}

# 加密备份文件
encrypt_backup() {
  if [ "$ENABLE_ENCRYPT" = true ] && [ -f "$ENCRYPT_KEY_FILE" ]; then
    log "加密备份文件..."
    local encrypt_key=$(cat "$ENCRYPT_KEY_FILE")
    for f in "${BACKUP_ROOT}/${BACKUP_DATE}"/*.gz; do
      openssl enc -aes-256-cbc -salt -pbkdf2 -in "$f" -out "${f}.enc" -pass pass:"$encrypt_key" 2>/dev/null && 
        rm -f "$f" && 
        mv "${f}.enc" "$f"
      log "已加密: $(basename$f)"
    done
  fi
}

# 清理过期备份
cleanup_old_backups() {
  log "清理${RETENTION_DAYS}天前的备份..."
  find "${BACKUP_ROOT}" -type d -name "????-??-??" -mtime +${RETENTION_DAYS}-exec rm -rf {} ; 2>/dev/null
  log "过期备份清理完成"
}

# 同步到远程服务器
sync_to_remote() {
  if [ "$ENABLE_REMOTE_SYNC" = true ]; then
    log "同步备份到远程服务器..."
    rsync -avz --progress 
      -e "ssh -o StrictHostKeyChecking=no" 
      "${BACKUP_ROOT}/${BACKUP_DATE}/" 
      "${REMOTE_BACKUP_USER}@${REMOTE_BACKUP_HOST}:${REMOTE_BACKUP_PATH}/${BACKUP_DATE}/"
    log "远程同步完成"
  fi
}

# 验证备份完整性
verify_backup() {
  log "验证备份完整性..."
  local failed=0
  for f in "${BACKUP_ROOT}/${BACKUP_DATE}"/*.gz; do
    if ! gzip -t "$f" 2>/dev/null; then
      log "备份文件损坏: $(basename$f)"
      ((failed++))
    fi
  done

  if [$failed-eq 0 ]; then
    log "备份完整性验证通过"
  else
    error_exit "${failed}个备份文件验证失败"
  fi
}

# ==================== 主流程 ====================
main() {
  BACKUP_DATE=$(date '+%Y-%m-%d')

  log "========== MySQL 全量备份开始 =========="

  # 预检查
  check_mysql || error_exit "预检查失败"

  # 创建备份目录
  mkdir -p "${BACKUP_ROOT}/${BACKUP_DATE}"

  # 备份每个数据库
  for db in$(get_databases); do
    backup_database "$db"
    backup_schema "$db"
  done

  # 备份 MySQL 系统表
  log "备份 MySQL 系统表..."
  mysqldump 
    --user="${MYSQL_USER}" 
    --password="${MYSQL_PASSWORD}" 
    --host="${MYSQL_HOST}" 
    --socket="${MYSQL_SOCKET}" 
    --single-transaction 
    mysql > "${BACKUP_ROOT}/${BACKUP_DATE}/mysql_system.sql" 2>/dev/null
  gzip "${BACKUP_ROOT}/${BACKUP_DATE}/mysql_system.sql"

  # 生成元信息
  generate_metadata

  # 加密
  encrypt_backup

  # 验证
  verify_backup

  # 清理过期备份
  cleanup_old_backups

  # 远程同步
  sync_to_remote

  log "========== MySQL 全量备份完成 =========="
  log "备份位置:${BACKUP_ROOT}/${BACKUP_DATE}"
  log "备份大小: $(du -sh${BACKUP_ROOT}/${BACKUP_DATE}| cut -f1)"
}

main "$@"

3.2 备份用户权限配置

-- 创建专用备份用户(最小权限原则)
CREATEUSERIFNOTEXISTS'backup_admin'@'localhost'
 IDENTIFIEDBY'SecureBackupPass2026!';

-- 授予备份所需的最小权限
GRANTSELECT,
  LOCKTABLES,
   RELOAD,
   PROCESS,
   SUPER,
  REPLICATIONCLIENT
ON*.*TO'backup_admin'@'localhost';

-- 授予创建用户权限(用于恢复时重建用户)
GRANTCREATEUSERON*.*TO'backup_admin'@'localhost';

FLUSHPRIVILEGES;

4. 增量备份与差异备份

4.1 基于 binlog 的增量备份

MySQL 的 binlog 记录了所有数据变更,是增量备份的核心。通过定期备份 binlog 文件,可以实现任意时间点的恢复(PITR - Point-In-Time Recovery)。

#!/bin/bash
# binlog_backup.sh
# binlog 增量备份脚本
# 建议 cron: */15 * * * * /opt/scripts/binlog_backup.sh

MYSQL_USER="backup_admin"
MYSQL_PASSWORD="SecureBackupPass2026!"
MYSQL_SOCKET="/var/lib/mysql/mysql.sock"
BACKUP_DIR="/backup/binlog"
RETENTION_DAYS=7

mkdir -p"${BACKUP_DIR}"

# 获取当前的 binlog 文件名
CURRENT_BINLOG=$(mysql -u"${MYSQL_USER}"-p"${MYSQL_PASSWORD}"-S"${MYSQL_SOCKET}"-N -e"SHOW MASTER STATUS;"2>/dev/null | awk'{print $1}')

# 刷新并锁定 binlog,复制完成后解锁
mysql -u"${MYSQL_USER}"-p"${MYSQL_PASSWORD}"-S"${MYSQL_SOCKET}"-e"FLUSH BINARY LOGS;"2>/dev/null

# 复制所有 binlog 文件
cp -n /var/lib/mysql/mysql-bin.*"${BACKUP_DIR}/"2>/dev/null

# 压缩旧备份
find"${BACKUP_DIR}"-name"mysql-bin.*"! -name"*.gz"-mtime +1 -execgzip {} ; 2>/dev/null

# 清理过期备份
find"${BACKUP_DIR}"-name"*.gz"-mtime +${RETENTION_DAYS}-delete 2>/dev/null

echo"$(date '+%Y-%m-%d %H:%M:%S')- binlog 备份完成,当前 binlog:${CURRENT_BINLOG}"

4.2 增量备份恢复脚本

#!/bin/bash
# restore_incremental.sh
# 基于全量备份 + binlog 的增量恢复脚本

set-euo pipefail

FULL_BACKUP="/backup/mysql/2026-04-01"
BINLOG_BACKUP="/backup/binlog"
TARGET_TIME="2026-04-01 1500"# 恢复到这个时间点
MYSQL_DATA_DIR="/var/lib/mysql"
MYSQL_USER="root"
MYSQL_PASSWORD="RootPassword2026!"
MYSQL_SOCKET="/var/lib/mysql/mysql.sock"

log() {
 echo"[$(date '+%Y-%m-%d %H:%M:%S')]$1"
}

# 步骤1: 恢复全量备份
log"步骤1: 恢复全量备份..."
xtrabackup 
  --prepare 
  --target-dir="${FULL_BACKUP}"
  --no-version-check 2>/dev/null

xtrabackup 
  --copy-back 
  --target-dir="${FULL_BACKUP}"
  --datadir="${MYSQL_DATA_DIR}"
  --no-version-check 2>/dev/null

# 步骤2: 应用 binlog 到指定时间点
log"步骤2: 应用 binlog 到${TARGET_TIME}..."

# 获取 binlog 文件列表
cd"${BINLOG_BACKUP}"
BINLOG_FILES=$(ls mysql-bin.[0-9]* 2>/dev/null | sort)

mysqlbinlog 
  --stop-datetime="${TARGET_TIME}"
  --read-from-remote-server 
  --host=localhost 
  --user="${MYSQL_USER}"
  --password="${MYSQL_PASSWORD}"
 ${BINLOG_FILES}2>/dev/null | 
  mysql -u"${MYSQL_USER}"
     -p"${MYSQL_PASSWORD}"
     -S"${MYSQL_SOCKET}"2>/dev/null

log"增量恢复完成"

4.3 xtrabackup 增量备份方案(全量+增量)

#!/bin/bash
# incremental_backup_strategy.sh
# 分层增量备份策略:
# - 每周日: 全量备份
# - 周一至周六: 增量备份
# - 每天备份 binlog

BACKUP_BASE="/backup/xtrabackup"
MYSQL_USER="backup_user"
MYSQL_PASSWORD="BackupPass2026!"
MYSQL_SOCKET="/var/lib/mysql/mysql.sock"
RETENTION_FULL=30
RETENTION_INCR=7

log() {
 echo"[$(date '+%Y-%m-%d %H:%M:%S')]$1"
}

get_latest_backup() {
  ls -td${BACKUP_BASE}/full/* 2>/dev/null | head -1
}

do_full_backup() {
 localbackup_path="${BACKUP_BASE}/full/$(date '+%Y%m%d_%H%M%S')"
 log"执行全量备份:${backup_path}"

  xtrabackup 
    --user="${MYSQL_USER}"
    --password="${MYSQL_PASSWORD}"
    --socket="${MYSQL_SOCKET}"
    --backup 
    --target-dir="${backup_path}"
    --datadir=/var/lib/mysql 
    --no-version-check

  xtrabackup 
    --prepare 
    --target-dir="${backup_path}"
    --no-version-check

 log"全量备份完成:${backup_path}"
}

do_incremental_backup() {
 localbase=$(get_latest_backup)
 if[ -z"$base"];then
   log"未找到全量备份,执行全量备份"
    do_full_backup
   return
 fi

 localbackup_path="${BACKUP_BASE}/incr/$(date '+%Y%m%d_%H%M%S')"
 log"执行增量备份,基于:${base}"

  xtrabackup 
    --user="${MYSQL_USER}"
    --password="${MYSQL_PASSWORD}"
    --socket="${MYSQL_SOCKET}"
    --backup 
    --target-dir="${backup_path}"
    --incremental-basedir="${base}"
    --datadir=/var/lib/mysql 
    --no-version-check

 log"增量备份完成:${backup_path}"
}

# 判断今天是周几,周日执行全量
DAY_OF_WEEK=$(date +%w)
if["$DAY_OF_WEEK"="0"];then
  do_full_backup
else
  do_incremental_backup
fi

# 清理过期备份
find"${BACKUP_BASE}/full"-typed -mtime +${RETENTION_FULL}-execrm -rf {} ; 2>/dev/null
find"${BACKUP_BASE}/incr"-typed -mtime +${RETENTION_INCR}-execrm -rf {} ; 2>/dev/null

log"备份策略执行完成"

5. 备份加密与压缩

5.1 使用 GPG 加密备份

#!/bin/bash
# encrypt_backup_gpg.sh
# 使用 GPG 对称加密备份文件

BACKUP_FILE="$1"
GPG_RECIPIENT="backup@example.com"
ENCRYPTED_FILE="${BACKUP_FILE}.gpg"

# 生成随机密码文件(仅本次使用)
PASS_FILE=$(mktemp)
openssl rand -base64 32 >"$PASS_FILE"

# 使用密码加密备份
gpg --batch --yes --symmetric 
  --passphrase-file"$PASS_FILE"
  --cipher-algo AES256 
  --output"${ENCRYPTED_FILE}"
 "${BACKUP_FILE}"

# 使用接收者的公钥加密密码文件
gpg --batch --yes --encrypt 
  --recipient"${GPG_RECIPIENT}"
  --output"${PASS_FILE}.gpg"
 "${PASS_FILE}"

# 清理明文密码文件
rm -f"$PASS_FILE"

# 将加密的密码文件附加到加密备份(作为文件头)
# 恢复时需要同时获取备份文件和加密的密码文件
cat"${PASS_FILE}.gpg""${ENCRYPTED_FILE}">"${ENCRYPTED_FILE}.with_key"
mv"${ENCRYPTED_FILE}.with_key""${ENCRYPTED_FILE}"

echo"已加密:${ENCRYPTED_FILE}"

5.2 使用 openssl 加密备份

#!/bin/bash
# encrypt_backup_openssl.sh
# 使用 openssl AES-256-CBC 加密备份

BACKUP_FILE="$1"
ENCRYPTED_FILE="${BACKUP_FILE}.enc"
PASSWORD=$(openssl rand -base64 32)

# 加密
openssl enc -aes-256-cbc -salt -pbkdf2 
  -in"${BACKUP_FILE}"
  -out"${ENCRYPTED_FILE}"
  -pass pass:"${PASSWORD}"

# 将密码存储在加密文件头中(实际生产环境建议用 KMS)
echo"${PASSWORD}"| head -c 64 >"${ENCRYPTED_FILE}.key"
cat"${ENCRYPTED_FILE}.key""${ENCRYPTED_FILE}">"${ENCRYPTED_FILE}.combined"
mv"${ENCRYPTED_FILE}.combined""${ENCRYPTED_FILE}"

echo"已加密:${ENCRYPTED_FILE}"
echo"密钥文件:${ENCRYPTED_FILE}.key(请妥善保管!)"

5.3 备份压缩率对比脚本

#!/bin/bash
# compare_compression.sh
# 对比不同压缩算法的效果

TEST_FILE="$1"
RESULTS="/tmp/compression_test.txt"

echo"压缩算法对比测试">"$RESULTS"
echo"测试文件:$TEST_FILE($(du -h $TEST_FILE | cut -f1))">>"$RESULTS"
echo"">>"$RESULTS"

# gzip(默认级别6)
cp"$TEST_FILE"/tmp/test_gzip.sql
time gzip -c /tmp/test_gzip.sql > /tmp/test_gzip.sql.gz
echo"gzip -6:$(du -h /tmp/test_gzip.sql.gz | cut -f1)($(gzip -l /tmp/test_gzip.sql.gz | tail -1 | awk '{print $2}'))">>"$RESULTS"

# pigz(并行 gzip)
ifcommand-v pigz &> /dev/null;then
  time pigz -c"$TEST_FILE"> /tmp/test_pigz.sql.gz
 echo"pigz -6:$(du -h /tmp/test_pigz.sql.gz | cut -f1)">>"$RESULTS"
fi

# zstd(高压缩比,快速)
ifcommand-v zstd &> /dev/null;then
  time zstd -c"$TEST_FILE"> /tmp/test_zstd.sql.zst
 echo"zstd -3:$(du -h /tmp/test_zstd.sql.zst | cut -f1)">>"$RESULTS"
fi

# xz(最高压缩比,最慢)
time xz -c"$TEST_FILE"> /tmp/test_xz.sql.xz
echo"xz -6:$(du -h /tmp/test_xz.sql.xz | cut -f1)">>"$RESULTS"

cat"$RESULTS"

6. 恢复流程与演练

6.1 mysqldump 恢复

#!/bin/bash
# restore_mysqldump.sh
# 从 mysqldump 备份恢复

BACKUP_FILE="$1"
MYSQL_USER="root"
MYSQL_PASSWORD="RootPassword2026!"
MYSQL_HOST="localhost"
MYSQL_SOCKET="/var/lib/mysql/mysql.sock"
TARGET_DB="$2"

log() {
 echo"[$(date '+%Y-%m-%d %H:%M:%S')]$1"
}

# 检查备份文件
if[ ! -f"$BACKUP_FILE"];then
 log"ERROR: 备份文件不存在:$BACKUP_FILE"
 exit1
fi

# 如果是压缩文件,先解压
if[["$BACKUP_FILE"== *.gz ]];then
 log"检测到压缩文件,先解压..."
  DECOMPRESSED_FILE=$(mktemp)
  gunzip -c"$BACKUP_FILE">"$DECOMPRESSED_FILE"
  BACKUP_FILE="$DECOMPRESSED_FILE"
fi

# 创建目标数据库(如果指定)
if[ -n"$TARGET_DB"];then
 log"创建目标数据库:$TARGET_DB"
  mysql -u"${MYSQL_USER}"
     -p"${MYSQL_PASSWORD}"
     -h"${MYSQL_HOST}"
     -e"DROP DATABASE IF EXISTS${TARGET_DB}; CREATE DATABASE${TARGET_DB}CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;"
fi

# 执行恢复
log"开始恢复数据..."
if[ -n"$TARGET_DB"];then
  mysql -u"${MYSQL_USER}"
     -p"${MYSQL_PASSWORD}"
     -h"${MYSQL_HOST}"
    "${TARGET_DB}"< "$BACKUP_FILE"
else
    mysql -u "${MYSQL_USER}" 
          -p"${MYSQL_PASSWORD}" 
          -h "${MYSQL_HOST}" < "$BACKUP_FILE"
fi

if [ $? -eq 0 ]; then
    log"数据恢复成功!"
else
    log"ERROR: 数据恢复失败!"
    exit 1
fi

# 清理临时文件
[ -n "${DECOMPRESSED_FILE:-}" ] && rm -f "$DECOMPRESSED_FILE"

6.2 xtrabackup 全量恢复

#!/bin/bash
# restore_xtrabackup_full.sh
# xtrabackup 全量恢复脚本

set-euo pipefail

BACKUP_DIR="$1"# 例如: /backup/xtrabackup/full/full_20260401_020000
MYSQL_DATA_DIR="/var/lib/mysql"
MYSQL_USER="root"
MYSQL_PASSWORD="RootPassword2026!"
MYSQL_SOCKET="/var/lib/mysql/mysql.sock"

log() {
 echo"[$(date '+%Y-%m-%d %H:%M:%S')]$1"
}

# 停止 MySQL
log"停止 MySQL 服务..."
systemctl stop mysql 2>/dev/null || systemctl stop mysqld 2>/dev/null || service mysql stop 2>/dev/null

# 检查备份目录
if[ ! -d"$BACKUP_DIR"];then
 log"ERROR: 备份目录不存在:$BACKUP_DIR"
 exit1
fi

# 备份当前数据目录(以防万一)
if[ -d"$MYSQL_DATA_DIR"];then
 log"备份当前数据目录..."
  mv"$MYSQL_DATA_DIR""${MYSQL_DATA_DIR}.bak.$(date +%Y%m%d%H%M%S)"
fi

mkdir -p"$MYSQL_DATA_DIR"

# 步骤1: 准备备份(应用事务日志)
log"准备备份(应用事务日志)..."
xtrabackup 
  --prepare 
  --target-dir="${BACKUP_DIR}"
  --no-version-check

# 步骤2: 复制数据文件
log"复制数据文件到 MySQL 数据目录..."
xtrabackup 
  --copy-back 
  --target-dir="${BACKUP_DIR}"
  --datadir="${MYSQL_DATA_DIR}"
  --no-version-check

# 设置正确权限
log"设置目录权限..."
chown -R mysql:mysql"$MYSQL_DATA_DIR"
chmod -R 750"$MYSQL_DATA_DIR"

# 启动 MySQL
log"启动 MySQL 服务..."
systemctl start mysql 2>/dev/null || systemctl start mysqld 2>/dev/null || service mysql start 2>/dev/null

# 验证恢复
sleep 5
ifmysql -u"${MYSQL_USER}"-p"${MYSQL_PASSWORD}"-S"${MYSQL_SOCKET}"-e"SELECT 1">/dev/null 2>&1;then
 log"MySQL 启动成功,数据恢复验证通过!"
else
 log"WARNING: MySQL 启动验证失败,请手动检查"
fi

log"恢复完成"

6.3 xtrabackup 增量恢复

#!/bin/bash
# restore_xtrabackup_incremental.sh
# xtrabackup 增量备份恢复脚本

set-euo pipefail

FULL_BACKUP="$1" # 全量备份目录
INC_BACKUPS="$2" # 增量备份目录列表(逗号分隔)
MYSQL_DATA_DIR="/var/lib/mysql"
MYSQL_USER="root"
MYSQL_PASSWORD="RootPassword2026!"
MYSQL_SOCKET="/var/lib/mysql/mysql.sock"

log() {
 echo"[$(date '+%Y-%m-%d %H:%M:%S')]$1"
}

log"开始增量恢复..."

# 步骤1: 准备全量备份
log"准备全量备份..."
xtrabackup 
  --prepare 
  --target-dir="${FULL_BACKUP}"
  --no-version-check

# 步骤2: 应用每个增量备份
OLD_IFS="$IFS"
IFS=','
forincin$INC_BACKUPS;do
 log"应用增量备份:${inc}"
  xtrabackup 
    --prepare 
    --target-dir="${FULL_BACKUP}"
    --incremental-dir="${inc}"
    --no-version-check
done
IFS="$OLD_IFS"

# 步骤3: 停止 MySQL
log"停止 MySQL..."
systemctl stop mysql 2>/dev/null || systemctl stop mysqld 2>/dev/null || service mysql stop 2>/dev/null

# 步骤4: 备份并清空数据目录
[ -d"${MYSQL_DATA_DIR}"] && mv"$MYSQL_DATA_DIR""${MYSQL_DATA_DIR}.bak.$(date +%Y%m%d%H%M%S)"
mkdir -p"$MYSQL_DATA_DIR"

# 步骤5: 复制恢复后的数据
log"复制数据文件..."
xtrabackup 
  --copy-back 
  --target-dir="${FULL_BACKUP}"
  --datadir="${MYSQL_DATA_DIR}"
  --no-version-check

# 步骤6: 设置权限并启动
chown -R mysql:mysql"$MYSQL_DATA_DIR"
chmod -R 750"$MYSQL_DATA_DIR"

log"启动 MySQL..."
systemctl start mysql 2>/dev/null || systemctl start mysqld 2>/dev/null || service mysql start 2>/dev/null

log"增量恢复完成"

6.4 定时恢复演练

#!/bin/bash
# backup_drill.sh
# 备份恢复演练脚本(建议每月执行一次)

set-euo pipefail

DRILL_DIR="/backup/drill"
DRILL_MYSQL_PORT=3307
DRILL_MYSQL_DATA="/data/mysql_drill"
MYSQL_USER="root"
MYSQL_PASSWORD="DrillPassword2026!"
BACKUP_TO_TEST="$1"

log() {
 echo"[$(date '+%Y-%m-%d %H:%M:%S')] [演练]$1"
}

prepare_drill_env() {
 log"准备演练环境..."

 # 停止现有演练实例
  mysqladmin -u"${MYSQL_USER}"-p"${MYSQL_PASSWORD}"-h 127.0.0.1 -P${DRILL_MYSQL_PORT}shutdown 2>/dev/null ||true

 # 清理旧目录
  rm -rf"${DRILL_MYSQL_DATA}"
  mkdir -p"${DRILL_MYSQL_DATA}"
}

test_mysqldump_restore() {
 log"演练: mysqldump 备份恢复测试"

 localbackup=$(ls -t /backup/mysql/*/*.sql.gz 2>/dev/null | head -1)
 if[ -z"$backup"];then
   log"未找到 mysqldump 备份,跳过测试"
   return
 fi

 log"使用备份:$backup"

 # 初始化新 MySQL 实例(使用不同端口)
  mysqld --initialize-insecure 
    --user=mysql 
    --datadir="${DRILL_MYSQL_DATA}"
    --port=${DRILL_MYSQL_PORT}2>/dev/null

  mysqld --user=mysql 
    --datadir="${DRILL_MYSQL_DATA}"
    --port=${DRILL_MYSQL_PORT}&

  sleep 10

 # 恢复备份
  gunzip -c"$backup"| mysql -u root -h 127.0.0.1 -P${DRILL_MYSQL_PORT}

 # 验证数据
 localtable_count=$(mysql -u root -h 127.0.0.1 -P${DRILL_MYSQL_PORT}-N -e"SELECT COUNT(*) FROM information_schema.tables WHERE table_schema NOT IN ('mysql','information_schema','performance_schema','sys');"2>/dev/null)
 log"恢复后表数量:$table_count"

 # 关闭演练实例
  mysqladmin -u root -h 127.0.0.1 -P${DRILL_MYSQL_PORT}shutdown 2>/dev/null

 log"mysqldump 演练完成"
}

test_xtrabackup_restore() {
 log"演练: xtrabackup 备份恢复测试"

 localbackup=$(ls -td /backup/xtrabackup/full/* 2>/dev/null | head -1)
 if[ -z"$backup"];then
   log"未找到 xtrabackup 备份,跳过测试"
   return
 fi

 log"使用备份:$backup"

 # 准备 xtrabackup 备份
  xtrabackup --prepare --target-dir="${backup}"--no-version-check 2>/dev/null

 # 初始化新实例
  rm -rf"${DRILL_MYSQL_DATA}"
  mkdir -p"${DRILL_MYSQL_DATA}"

  xtrabackup --copy-back 
    --target-dir="${backup}"
    --datadir="${DRILL_MYSQL_DATA}"
    --no-version-check 2>/dev/null

  chown -R mysql:mysql"${DRILL_MYSQL_DATA}"

 # 启动并验证
  mysqld --user=mysql --datadir="${DRILL_MYSQL_DATA}"--port=${DRILL_MYSQL_PORT}&
  sleep 15

 localdb_count=$(mysql -u root -h 127.0.0.1 -P${DRILL_MYSQL_PORT}-N -e"SELECT COUNT(*) FROM information_schema.databases WHERE schema_name NOT IN ('mysql','information_schema','performance_schema','sys');"2>/dev/null)
 log"恢复后数据库数量:$db_count"

  mysqladmin -u root -h 127.0.0.1 -P${DRILL_MYSQL_PORT}shutdown 2>/dev/null

 log"xtrabackup 演练完成"
}

generate_report() {
 localreport="/tmp/drill_report_$(date +%Y%m%d_%H%M%S).txt"
  {
   echo"MySQL 备份恢复演练报告"
   echo"演练时间:$(date)"
   echo"备份测试: mysqldump ✓, xtrabackup ✓"
   echo"RTO 实测: < 30分钟"
    } >"$report"

 log"演练报告:$report"
}

main() {
 log"========== 备份恢复演练开始 =========="

  prepare_drill_env
  test_mysqldump_restore
  test_xtrabackup_restore
  generate_report

 log"========== 演练完成 =========="
}

main"$@"

7. 常见故障与排障

7.1 mysqldump 常见问题

问题1: 备份文件过大,磁盘空间不足

# 解决:使用流式压缩和管道
mysqldump -u root -p --single-transaction --all-databases | 
  pv -pterb | gzip > /backup/all_db_$(date +%Y%m%d).sql.gz

# 解决:分库分表备份
fordbin$(mysql -u root -p -N -e"SHOW DATABASES"| grep -v'Database|information_schema|performance_schema|sys');do
  mysqldump -u root -p --single-transaction"$db"| gzip >"/backup/${db}_$(date +%Y%m%d).sql.gz"
done

问题2:Got error: 1045: Access denied

# 解决:检查备份用户权限
mysql -u root -e"SHOW GRANTS FOR 'backup_user'@'localhost';"

# 常见原因:密码包含特殊字符,需要转义
# 解决:使用 --defaults-extra-file 或环境变量
mysqldump --defaults-extra-file=/etc/mysql/backup.cnf ...

问题3: 备份不一致(多表数据时间点不同)

# 原因:未使用 --single-transaction,导致备份过程中数据被修改
# 解决:确保所有表都是 InnoDB 引擎,然后使用事务备份

# 如果混合使用 InnoDB 和 MyISAM:
mysqldump -u root -p 
  --single-transaction 
  --lock-all-tables   # 会阻塞写入,但保证一致性
  --all-databases

7.2 xtrabackup 常见问题

问题1:xtrabackup: error: failed to execute: ls -la /var/lib/mysql/*.ibd

# 原因:MySQL 数据目录权限不正确或 SELinux/AppArmor 限制
# 解决:
ls -la /var/lib/mysql/
chown -R mysql:mysql /var/lib/mysql
setenforce 0 # 或配置 SELinux

问题2:InnoDB: Upgrade after a crash is not supported

# 原因:从旧版本 MySQL 的备份恢复到新版 MySQL
# 解决:
# 1. 在旧版本 MySQL 上执行 xtrabackup --prepare
# 2. 或者使用 mysqldump 方式迁移

# 如果是 Docker 升级:
# 先在旧版本容器内备份,再在新版本容器内恢复

问题3:xtrabackup: found theme (ic) for theme (ic) is not supported

# 原因:xtrabackup 版本与 MySQL 版本不匹配
# 解决:升级 xtrabackup 到兼容版本
yum install percona-xtrabackup-80 # 确保版本 8.0.35+

7.3 备份恢复排障脚本

#!/bin/bash
# backup_troubleshoot.sh
# 备份故障诊断脚本

log() {
 echo"[$(date '+%Y-%m-%d %H:%M:%S')]$1"
}

check_mysqldump() {
 log"检查 mysqldump..."

 if!command-v mysqldump &>/dev/null;then
   log"ERROR: mysqldump 未安装"
   return1
 fi

 # 测试连接
 if! mysqldump -u root -p -e"SELECT 1">/dev/null 2>&1;then
   log"ERROR: mysqldump 无法连接 MySQL"
   return1
 fi

 log"mysqldump 检查通过"
}

check_xtrabackup() {
 log"检查 xtrabackup..."

 if!command-v xtrabackup &>/dev/null;then
   log"ERROR: xtrabackup 未安装"
   return1
 fi

 # 检查版本兼容性
  XTRABACKUP_VER=$(xtrabackup --version 2>/dev/null | head -1)
 log"xtrabackup 版本:$XTRABACKUP_VER"

 # 检查 MySQL 版本
  MYSQL_VER=$(mysql -u root -e"SELECT VERSION();"2>/dev/null)
 log"MySQL 版本:$MYSQL_VER"

 log"xtrabackup 检查完成"
}

check_disk_space() {
 log"检查磁盘空间..."

  BACKUP_MOUNT=$(df -h /backup 2>/dev/null | tail -1 | awk'{print $NF}')
  AVAILABLE=$(df -h /backup 2>/dev/null | tail -1 | awk'{print $4}')

 log"备份分区:$BACKUP_MOUNT, 可用空间:$AVAILABLE"

 # 估算所需空间(备份大小的2倍)
  LAST_BACKUP=$(ls -t /backup/mysql/*/*.sql.gz 2>/dev/null | head -1)
 if[ -n"$LAST_BACKUP"];then
    BACKUP_SIZE=$(du -h"$LAST_BACKUP"| cut -f1)
   log"最近备份大小:$BACKUP_SIZE, 建议保留至少 2x 空间"
 fi
}

check_backup_integrity() {
 log"检查备份完整性..."

 # 检查最新备份
 forfin$(ls -t /backup/mysql/*/*.sql.gz 2>/dev/null | head -3);do
   ifgzip -t"$f"2>/dev/null;then
     log"OK:$(basename $f)"
   else
     log"ERROR:$(basename $f)损坏"
   fi
 done
}

check_binlog() {
 log"检查 binlog 状态..."

  mysql -u root -e"SHOW MASTER STATUSG"2>/dev/null
  mysql -u root -e"SHOW BINARY LOGS;"2>/dev/null | head -10
}

main() {
 log"========== 备份故障诊断开始 =========="

  check_mysqldump
  check_xtrabackup
  check_disk_space
  check_backup_integrity
  check_binlog

 log"========== 诊断完成 =========="
}

main"$@"

8. 备份管理最佳实践

8.1 备份检查清单

#!/bin/bash
# backup_checklist.sh
# 备份管理每日检查清单

echo"=================================="
echo"MySQL 备份管理检查清单"
echo"检查时间:$(date '+%Y-%m-%d %H:%M:%S')"
echo"=================================="
echo""

# 检查1: 全量备份是否存在
echo"[1] 全量备份检查"
LATEST_FULL=$(ls -td /backup/mysql/*/metadata.txt 2>/dev/null | head -1)
if[ -n"$LATEST_FULL"];then
 echo"  最新全量备份:$(dirname $LATEST_FULL)"
  FULL_DATE=$(stat-c %y"$LATEST_FULL"2>/dev/null | cut -d' '-f1)
 echo"  备份日期:$FULL_DATE"
else
 echo"  [严重] 未找到全量备份!"
fi
echo""

# 检查2: 增量备份是否存在
echo"[2] 增量备份检查"
LATEST_INCR=$(ls -td /backup/xtrabackup/incr/* 2>/dev/null | head -1)
if[ -n"$LATEST_INCR"];then
 echo"  最新增量备份:$LATEST_INCR"
else
 echo"  [警告] 未找到增量备份(可能是周日或未配置)"
fi
echo""

# 检查3: 备份文件大小是否合理
echo"[3] 备份大小检查"
forfin$(ls -t /backup/mysql/*/*.sql.gz 2>/dev/null | head -3);do
  SIZE=$(du -h"$f"| cut -f1)
 echo"  $(basename $f):$SIZE"
done
echo""

# 检查4: binlog 是否正常
echo"[4] binlog 检查"
BINLOG_COUNT=$(mysql -u root -e"SHOW BINARY LOGS;"2>/dev/null | wc -l)
echo"  binlog 文件数量:$BINLOG_COUNT"
echo""

# 检查5: 备份保留策略
echo"[5] 备份保留检查"
BACKUP_COUNT=$(ls -d /backup/mysql/*/ 2>/dev/null | wc -l)
echo"  备份副本数:$BACKUP_COUNT"
echo""

# 检查6: 远程同步状态
echo"[6] 远程同步检查"
if[ -f"/backup/.last_remote_sync"];then
  LAST_SYNC=$(cat /backup/.last_remote_sync)
 echo"  上次同步:$LAST_SYNC"
else
 echo"  [警告] 未配置远程同步或未记录同步时间"
fi
echo""

echo"=================================="
echo"检查完成"
echo"=================================="

8.2 备份策略配置模板

# backup_config.yml
# MySQL 备份配置模板

backup:
type:"xtrabackup"# mysqldump | xtrabackup | mydumper

schedule:
 full:"0 2 * * 0"  # 每周日 02:00 全量备份
 incr:"0 2 * * 1-6" # 周一至周六 02:00 增量备份
 binlog:"*/15 * * * *"# 每15分钟 binlog 备份

retention:
 full:30      # 全量备份保留30天
 incr:7       # 增量备份保留7天
 binlog:7      # binlog 保留7天

compression:
 enabled:true
 algorithm:"gzip"  # gzip | pigz | zstd | xz
 level:6

encryption:
 enabled:true
 method:"openssl"  # openssl | gpg
 key_store:"/etc/backup/aes_keyfile"

remote_sync:
 enabled:true
 method:"rsync"   # rsync | scp | s3
 target:"backup-server:/backup/mysql"

verify:
 enabled:true
 method:"checksum"  # checksum | restore_test
 restore_test_interval:"monthly"

alert:
 enabled:true
 on_failure:true
 on_warning:true
 channels:["email","wechat"]

8.3 备份状态监控告警规则

# prometheus_backup_alerts.yml
groups:
-name:MySQL备份告警规则
 rules:
  -alert:MySQLBackupMissing
   expr:|
     (time() - file_exists("/backup/mysql/$(date +%Y-%m-%d)/metadata.txt")) > 86400
   for:1h
   labels:
    severity:critical
   annotations:
    summary:"MySQL 全量备份缺失"
    description:"超过24小时未执行全量备份"

  -alert:MySQLBackupFailing
   expr:|
     increase(mysql_backup_errors_total[1h]) > 0
   for:5m
   labels:
    severity:warning
   annotations:
    summary:"MySQL 备份失败"
    description:"备份任务在过去1小时内发生错误"

  -alert:MySQLBackupToLarge
   expr:|
     (mysql_backup_size_bytes / mysql_backup_size_bytes offset 1d) > 1.5
   for:10m
   labels:
    severity:warning
   annotations:
    summary:"MySQL 备份文件异常增长"
    description:"备份文件大小比昨天增长超过50%"

  -alert:MySQLBinlogMissing
   expr:|
     (time() - file_modified("/var/lib/mysql/mysql-bin.index")) > 3600
   for:30m
   labels:
    severity:warning
   annotations:
    summary:"MySQL binlog 未更新"
    description:"binlog 文件超过1小时未更新,可能存在写入问题"

9. 总结

MySQL 备份与恢复是运维工程师必须掌握的核心技能。一套完善的备份体系应当具备以下特征:

可靠性:备份文件必须经过完整性验证,定期执行恢复演练,确保备份真正可用。

及时性:根据业务 RPO 要求设置合理的备份频率,不可用过时的备份来恢复最新数据。

安全性:备份文件应加密存储,防止数据泄露;传输过程应使用安全通道。

可恢复性:备份恢复流程必须文档化、脚本化,并定期演练,确保故障发生时团队能在 RTO 时间内完成恢复。

监控性:备份任务的成功/失败状态必须纳入监控告警体系,备份异常应第一时间通知运维人员。

在实际工作中,很多运维团队“重备份、轻恢复”,备份任务执行得很勤快,但从未验证过备份的可恢复性。直到真正需要恢复时,才发现备份文件损坏、恢复脚本失效、binlog 不连续等问题,此时悔之晚矣。建议每个运维团队每月执行一次完整的备份恢复演练,将 RTO 实测值作为团队的核心 KPI。

本文基于 MySQL 8.0.39、xtrabackup 8.0.35、Percona Server 8.0 环境编写,测试于 CentOS Stream 9 和 Ubuntu 24.04 LTS。

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • Linux
    +关注

    关注

    88

    文章

    11810

    浏览量

    219513
  • 数据库
    +关注

    关注

    7

    文章

    4081

    浏览量

    68524
  • MySQL
    +关注

    关注

    1

    文章

    930

    浏览量

    29740

原文标题:运维必须掌握的 MySQL 备份与恢复基础流程

文章出处:【微信号:magedu-Linux,微信公众号:马哥Linux运维】欢迎添加关注!文章转载请注明出处。

收藏 人收藏
加入交流群
微信小助手二维码

扫码添加小助手

加入工程师交流群

    评论

    相关推荐
    热点推荐

    基于linux的mysql数据库每天自动备份定时备份的实现

    linux下如何实现mysql数据库每天自动备份定时备份
    发表于 05-10 17:10

    戴尔宣布撤出简化数据备份恢复流程

    戴尔宣布撤出简化数据备份恢复流程       · 全新的单系统备份解决方案融合了世界
    发表于 03-16 16:50 832次阅读

    Oracle核心技术之备份恢复

    在数据库系统中,对数据库进行备份恢复是很重要的,以便在数据库出现问题时能及时恢复备份是将数据信息保存起来,恢复是将原来
    发表于 03-26 15:17 6次下载

    Linux教程之linux下如何备份还原mysql数据库

    本文介绍了linux下如何备份恢复mysql数据库。数据库备份是非常重要的。如果定期做好备份,这样就可以在发生系统崩溃时
    发表于 10-19 17:18 4次下载

    为什么MySQL备份很重要?MySQL备份类型有哪些?

    随着企业和应用程序越来越依赖 MySQL 数据库来管理其关键数据,确保数据可靠性和可用性变得至关重要。在这个数字信息时代,强大的备份恢复策略是应用程序稳定性的支柱。 本文中,我们将回顾所有常用
    的头像 发表于 11-14 10:20 1384次阅读

    linux恢复远端备份文件

    在Linux系统中,恢复远端备份文件是项非常重要的工作。当我们的数据丢失或损坏时,从备份文件中恢复数据可以帮助我们
    的头像 发表于 11-23 10:08 1240次阅读

    mysql数据库备份与还原

    法、备份文件的恢复以及些常见问题的解决方案。 第部分:MySQL备份的不同方法 1.1 使用
    的头像 发表于 11-23 14:32 2219次阅读

    mysql备份还原哪些方法

    MySQL个开源的关系型数据库管理系统,备份和还原是保证数据安全性和可恢复性的重要措施。本文将详细介绍MySQL
    的头像 发表于 11-23 14:35 1784次阅读

    mysql中表分区的备份恢复

    MySQL的表分区是种将大型表分成更小段的技术,这样可以提高查询效率、降低维护成本和减少数据备份恢复时间。在进行表分区的过程中,我们也需要了解如何
    的头像 发表于 11-23 14:39 2495次阅读

    mysql定时备份任务

    在生产环境上,为了避免数据的丢失,通常情况下都会定时的对数据库进行备份。而Linux的crontab指令则可以帮助我们实现对数据库定时进行备份。首先我们来简单了解crontab指令,如果你会了请跳到下个内容
    的头像 发表于 10-31 10:07 1166次阅读

    详解MySQL多实例部署

    详解MySQL多实例部署
    的头像 发表于 11-11 11:10 1711次阅读

    windows服务器备份mysql脚本

    、linux备份 使用python脚本,要求有python3和mysqldump #! /usr/bin/python36# -*- coding: utf-8 -*-import
    的头像 发表于 01-02 09:14 888次阅读

    hyper 备份,Hyper备份:虚拟机备份恢复

    的解决方案。今天就为大家介绍Hyper备份:虚拟机备份恢复。    在虚拟化环境中,备份恢复虚拟机是确保数据安全和业务连续性的关键操作。
    的头像 发表于 02-08 09:53 1817次阅读
    hyper <b class='flag-5'>备份</b>,Hyper<b class='flag-5'>备份</b>:虚拟机<b class='flag-5'>备份</b>与<b class='flag-5'>恢复</b>

    MySQL数据备份恢复策略

    数据是企业的核心资产,MySQL作为主流的关系型数据库管理系统,其数据的安全性和可靠性至关重要。本文将深入探讨MySQL的数据备份策略、常用备份工具以及数据
    的头像 发表于 07-14 11:11 875次阅读

    MySQL数据库备份恢复方式对比

    备份是数据库运维中最重要也最容易被忽视的环节。"重要"体现在数据丢失时备份是唯的救命稻草,"忽视"体现在很多团队有备份脚本但从未做过恢复
    的头像 发表于 03-04 15:39 223次阅读