背景
数据备份是数据库运维的最后一道防线。无论系统设计多么健壮、人为操作多么谨慎,硬件故障、软件 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运维】欢迎添加关注!文章转载请注明出处。
发布评论请先 登录
一文详解MySQL备份与恢复基础流程
评论