news 2026/5/26 19:59:23

MySQL 5.7/8.0 物理备份实战:XtraBackup 全量+增量+验证+恢复

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL 5.7/8.0 物理备份实战:XtraBackup 全量+增量+验证+恢复

在互联网业务里,数据不是“重要资产”,而是“生命线”。但很多团队做备份只做到“备份命令能跑通”,却没做到“出了事故能恢复”。这篇文章用 Percona XtraBackup 把 备份 → 验证 → 保留 → 恢复 的闭环讲清楚,并给出一份更接近生产可用的脚本模板。

主要从以下四点分析

- 如何选对 XtraBackup 版本(避坑第一步)

- 全量/增量的正确用法与恢复顺序

- 生产环境脚本该具备的“最低安全线”(锁、日志、保留、失败退出)

- 为什么“XtraBackup 无锁备份”不能理解成“完全无锁”

一、为什么选择 XtraBackup

优势确实存在:

- 在线备份(热备) :对 InnoDB 而言,可以在数据库运行中直接拷贝数据文件,并基于崩溃恢复机制保证一致性。

- 通常比逻辑备份更快 :它拷贝物理文件,不需要导出 SQL。

- 支持增量备份 :只备份变化的数据,节省存储和传输时间。

风险点也存在:

- “无锁备份”不是“绝对不加锁”。在 DDL、元数据变更、非事务表等场景下,仍可能出现 短暂的锁影响 。线上要做的是:把锁影响缩到最小、把 IO 影响可控,而不是相信“完全无影响”。

二、版本选择:最常见、也最致命的坑

- MySQL 5.7 → Percona XtraBackup 2.4.x(常见命令:innobackupex)

- MySQL 8.0 → Percona XtraBackup 8.0.x(常见命令:xtrabackup)

经验法则: MySQL 版本不匹配,备份可能“看似成功”,恢复才炸。

生产环境请先在测试机做一次完整恢复演练。

三、安装(以 CentOS/RHEL 为例)

sudo yum install https://repo.percona.com/yum/percona-release-latest.noarch.rpmsudo percona-release setup ps80sudo yum install percona-xtrabackup-80

MySQL 5.7 对应安装 percona-xtrabackup-24 (具体包名按仓库为准)。

四、日常全量备份(MySQL 8.0 示例)结尾有完整脚本​​​​​​​

xtrabackup --backup \--target-dir=/backup/full_$(date +%F_%H-%M) \--datadir=/var/lib/mysql

认证方式推荐放到 /etc/my.cnf 或 /root/.my.cnf ,不要在命令里明文写密码。

最低示例(根据你环境调整):​​​​​​​

[client]user=backup_userpassword=REDACTEDhost=127.0.0.1

五、增量备份与恢复顺序

5.1 增量备份怎么做

先做一次全量 /backup/full_xxx ,再做增量(基于上一次备份目录):​​​​​​​

xtrabackup --backup \--target-dir=/backup/inc_$(date +%F_%H-%M) \--incremental-basedir=/backup/full_xxx \--datadir=/var/lib/mysql

后续每次增量,都可以基于“上一份备份”(可能是全量也可能是增量),形成链路。

5.2 恢复时的 prepare 顺序(一定要对)

全量先 prepare,再按顺序合并每个增量,最后一次不加 apply-log-only:​​​​​​​

# 1) 先对全量做 prepare(保留 redo,便于合并增量)xtrabackup --prepare --apply-log-only --target-dir=/backup/full_xxx# 2) 依次合并每个增量(顺序不能错)xtrabackup --prepare --apply-log-only --target-dir=/backup/full_xxx --incremental-dir=/backup/inc_1xtrabackup --prepare --apply-log-only --target-dir=/backup/full_xxx --incremental-dir=/backup/inc_2# 3) 合并最后一个增量(最后一次不加 apply-log-only)xtrabackup --prepare --target-dir=/backup/full_xxx --incremental-dir=/backup/inc_last

六、生产级脚本:最低安全线应该包含什么?

我见过太多“能跑的脚本”,出事时恢复不了。生产脚本建议至少包含:

- 互斥锁 :避免并发备份(推荐 flock ,比 touch+trap 更不易死锁)

- 失败即退出 :备份失败不能继续清理旧备份

- 日志落盘 :方便排查(定时任务里尤为重要)

- 保留策略 :防止磁盘写满拖垮数据库

- IO 降权重 : nice/ionice 避免高峰期拖慢业务

- 可选保护 : chattr +i 可以用,但必须配套解锁与清理流程(否则你会删不掉旧备份)

下面给一个可发布的脚本模板(MySQL 5.7 以 innobackupex 为主):​​​​​​​

#!/bin/bash################################################################################# MySQL 5.7 生产环境备份脚本 (Percona XtraBackup 2.4)## Crontab 配置:# 30 1 * * * /bin/bash /home/backup/scripts/backup.sh full 2>&1 | logger -t mysql_backup# 0 */2 * * * /bin/bash /home/backup/scripts/backup.sh inc 2>&1 | logger -t mysql_backup################################################################################set -euo pipefailPATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin# ============ 核心配置 ============BACKUP_BASE_DIR="/home/backup/xtrabackup"INC_BASE_LIST="${BACKUP_BASE_DIR}/inc_list.txt"MYSQL_CNF="/etc/my.cnf"# 🔒 安全配置: 使用配置文件存储密码# 创建 /root/.my.cnf.backup 文件:# [client]# user=root# password=your_password# host=127.0.0.1MYSQL_CREDENTIAL_FILE="/root/.my.cnf.backup"# 备份工具 (MySQL 5.7 使用 innobackupex)XTRABACKUP_PATH="/usr/bin/innobackupex"# 性能配置THREAD=4WRAPPER_CMD="nice -n 19 ionice -c 3"# 锁文件和日志LOCK_FILE="/var/run/mysql_backup.lock"LOG_FILE="/var/log/mysql_backup.log"# 保留策略RETENTION_DAYS=7MAX_INC_COUNT=23 # 每天1次全量 + 每2小时1次增量 = 最多12次增量/天# 告警配置 (可选)ALERT_EMAIL="dba@example.com"ENABLE_EMAIL_ALERT=false# ============ 工具函数 ============log_msg() {local level="${2:-INFO}"echo "[$(date '+%Y-%m-%d %H:%M:%S')] [$level] $1" | tee -a "$LOG_FILE"}send_alert() {if [[ "$ENABLE_EMAIL_ALERT" == "true" ]]; thenecho "$1" | mail -s "MySQL备份告警 - $(hostname)" "$ALERT_EMAIL"fi}check_prerequisites() {# 检查工具是否存在if [[ ! -x "$XTRABACKUP_PATH" ]]; thenlog_msg "错误: 找不到 innobackupex,请安装 Percona XtraBackup 2.4" "ERROR"exit 1fi# 检查凭证文件if [[ ! -f "$MYSQL_CREDENTIAL_FILE" ]]; thenlog_msg "错误: 凭证文件不存在: $MYSQL_CREDENTIAL_FILE" "ERROR"log_msg "请创建文件并设置权限: chmod 600 $MYSQL_CREDENTIAL_FILE" "ERROR"exit 1fi# 检查目录权限mkdir -p "${BACKUP_BASE_DIR}"touch "${INC_BASE_LIST}" 2>/dev/null || {log_msg "错误: 无法写入备份目录" "ERROR"exit 1}}print_help() {cat << EOFMySQL 5.7 生产环境备份脚本━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━用法: $0 {full|inc|verify|help}命令:full - 执行全量备份inc - 执行增量备份 (无全量时自动转为全量)verify - 验证最新备份的完整性help - 显示此帮助信息配置文件位置:凭证: $MYSQL_CREDENTIAL_FILE日志: $LOG_FILE━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━EOFexit 0}# ============ 备份核心逻辑 ============FullBackup() {local start_time=$(date +%s)local CURRENT_BACKUP_PATH="${BACKUP_BASE_DIR}/$(date +%F_%H-%M-%S)_full"log_msg "==================== 开始全量备份 ===================="log_msg "目标路径: ${CURRENT_BACKUP_PATH}"mkdir -p "${CURRENT_BACKUP_PATH}"# 执行备份 (移除明文密码)${WRAPPER_CMD} ${XTRABACKUP_PATH} \--defaults-file=${MYSQL_CNF} \--defaults-extra-file=${MYSQL_CREDENTIAL_FILE} \--parallel=${THREAD} \--no-timestamp \--galera-info \--slave-info \"${CURRENT_BACKUP_PATH}" > "${CURRENT_BACKUP_PATH}/backup.log" 2>&1local backup_status=$?# 详细检查备份结果if [[ $backup_status -eq 0 ]] && grep -q "completed OK!" "${CURRENT_BACKUP_PATH}/backup.log"; thenlocal end_time=$(date +%s)local duration=$((end_time - start_time))log_msg "全量备份成功 (耗时: ${duration}秒)"# 记录到增量列表 (格式: 父目录|当前目录|类型|时间戳|大小)local backup_size=$(du -sh "${CURRENT_BACKUP_PATH}" | awk '{print $1}')chattr -a ${INC_BASE_LIST} 2>/dev/null || trueecho "NULL|${CURRENT_BACKUP_PATH}|full|$(date +%s)|${backup_size}" >> ${INC_BASE_LIST}chattr +a ${INC_BASE_LIST} 2>/dev/null || true# 锁定目录防止误删chattr -R +i "${CURRENT_BACKUP_PATH}" 2>/dev/null || truelog_msg "==================== 备份完成 ===================="return 0elselog_msg "全量备份失败! 状态码: $backup_status" "ERROR"log_msg "日志路径: ${CURRENT_BACKUP_PATH}/backup.log" "ERROR"send_alert "全量备份失败,请立即检查!主机: $(hostname)"return 1fi}IncBackup() {# 获取上一次备份路径local PREV_INFO=$(grep -v '^$' ${INC_BASE_LIST} 2>/dev/null | tail -1)local PREV_BACKUP_DIR=$(echo "$PREV_INFO" | awk -F '|' '{print $2}')# 检查是否需要转为全量备份if [[ -z "$PREV_BACKUP_DIR" || ! -d "$PREV_BACKUP_DIR" ]]; thenlog_msg "未找到有效的基础备份,转为全量备份" "WARN"FullBackupreturn $?fi# 检查增量备份链长度,避免链过长导致恢复缓慢local inc_count=$(grep -c "|inc|" ${INC_BASE_LIST} 2>/dev/null || echo 0)if [[ $inc_count -ge $MAX_INC_COUNT ]]; thenlog_msg "增量备份链过长 (${inc_count}次),强制执行全量备份" "WARN"FullBackupreturn $?filocal start_time=$(date +%s)local CURRENT_BACKUP_PATH="${BACKUP_BASE_DIR}/$(date +%F_%H-%M-%S)_inc"log_msg "==================== 开始增量备份 ===================="log_msg "目标路径: ${CURRENT_BACKUP_PATH}"log_msg "基准备份: ${PREV_BACKUP_DIR##*/}"mkdir -p "${CURRENT_BACKUP_PATH}"# 执行增量备份${WRAPPER_CMD} ${XTRABACKUP_PATH} \--defaults-file=${MYSQL_CNF} \--defaults-extra-file=${MYSQL_CREDENTIAL_FILE} \--parallel=${THREAD} \--no-timestamp \--incremental "${CURRENT_BACKUP_PATH}" \--incremental-basedir="${PREV_BACKUP_DIR}" > "${CURRENT_BACKUP_PATH}/backup.log" 2>&1local backup_status=$?if [[ $backup_status -eq 0 ]] && grep -q "completed OK!" "${CURRENT_BACKUP_PATH}/backup.log"; thenlocal end_time=$(date +%s)local duration=$((end_time - start_time))local backup_size=$(du -sh "${CURRENT_BACKUP_PATH}" | awk '{print $1}')log_msg "增量备份成功 (耗时: ${duration}秒, 大小: ${backup_size})"chattr -a ${INC_BASE_LIST} 2>/dev/null || trueecho "${PREV_BACKUP_DIR}|${CURRENT_BACKUP_PATH}|inc|$(date +%s)|${backup_size}" >> ${INC_BASE_LIST}chattr +a ${INC_BASE_LIST} 2>/dev/null || truechattr -R +i "${CURRENT_BACKUP_PATH}" 2>/dev/null || truelog_msg "==================== 备份完成 ===================="return 0elselog_msg "增量备份失败! 状态码: $backup_status" "ERROR"log_msg "日志路径: ${CURRENT_BACKUP_PATH}/backup.log" "ERROR"send_alert "增量备份失败,请立即检查!主机: $(hostname)"return 1fi}VerifyBackup() {local LATEST_BACKUP=$(grep -v '^$' ${INC_BASE_LIST} 2>/dev/null | tail -1 | awk -F '|' '{print $2}')if [[ -z "$LATEST_BACKUP" || ! -d "$LATEST_BACKUP" ]]; thenlog_msg "未找到备份需要验证" "ERROR"return 1filog_msg "开始验证备份: ${LATEST_BACKUP}"# 检查关键文件if [[ ! -f "${LATEST_BACKUP}/xtrabackup_checkpoints" ]]; thenlog_msg "验证失败: 缺少 xtrabackup_checkpoints 文件" "ERROR"return 1filog_msg "备份验证通过"log_msg "检查点信息:"cat "${LATEST_BACKUP}/xtrabackup_checkpoints" | tee -a "$LOG_FILE"return 0}CleanupOldBackups() {if [[ ! -d ${BACKUP_BASE_DIR} || "${BACKUP_BASE_DIR}" == "/" ]]; thenreturn 0filog_msg "开始清理 ${RETENTION_DAYS} 天前的备份..."local deleted_count=0find "${BACKUP_BASE_DIR}" -maxdepth 1 -type d -name "20*" -mtime +${RETENTION_DAYS} 2>/dev/null | while read dir; dolog_msg "删除过期备份: ${dir##*/}"chattr -R -i "$dir" 2>/dev/null || truerm -rf "$dir" && ((deleted_count++))done# 清理历史记录文件if [[ -f ${INC_BASE_LIST} ]]; thenlocal line_count=$(wc -l < ${INC_BASE_LIST})if [[ $line_count -gt 100 ]]; thenlog_msg "压缩历史记录 (${line_count} -> 100)"chattr -a ${INC_BASE_LIST} 2>/dev/null || truetail -n 100 ${INC_BASE_LIST} > ${INC_BASE_LIST}.tmp && mv ${INC_BASE_LIST}.tmp ${INC_BASE_LIST}chattr +a ${INC_BASE_LIST} 2>/dev/null || truefifilog_msg "清理完成"}# ============ 主程序 ============main() {[[ $# -eq 0 || "$1" == "help" ]] && print_helpcheck_prerequisites# 使用 flock 防止并发exec 9>"${LOCK_FILE}"if ! flock -n 9; thenlog_msg "另一个备份进程正在运行,退出" "WARN"exit 0filocal exit_code=0case "$1" infull)FullBackup || exit_code=$?[[ $exit_code -eq 0 ]] && CleanupOldBackups;;inc)if [[ ! -s ${INC_BASE_LIST} ]]; thenFullBackup || exit_code=$?elseIncBackup || exit_code=$?fi[[ $exit_code -eq 0 ]] && CleanupOldBackups;;verify)VerifyBackup || exit_code=$?;;*)print_help;;esacflock -u 9exit $exit_code}main "$@"

下面给一个可发布的脚本模板(以 MySQL 8.0 的 xtrabackup 为主;由于内容太多插入失败,有需要可留言私发

七、执行计划任务

根据你的实际情况更改增量备份时间​​​​​​​

# 每天凌晨1:30执行全量备份30 1 * * * /bin/bash /home/backup/scripts/backup.sh full 2>&1 | logger -t mysql_backup# 每2小时执行增量备份(业务高峰避开)0 3,5,7,9,11,13,15,17,19,21,23 * * * /bin/bash /home/backup/scripts/backup.sh inc 2>&1 | logger -t mysql_backup

八、备份验证:别让备份停在“我觉得能用”

强烈建议至少做到:

- 每周一次:对最近一次全量备份执行 --prepare (验证一致性)

- 每月一次:在测试环境完整 copy-back 恢复演练(验证可恢复性)

九)最佳实践:把备份当成体系,而不是命令

- 定期备份 :业务低谷执行,关键系统可更频繁做增量

- 异地存储 :本地备份负责“快速恢复”,异地备份负责“灾备”

- 权限隔离 :备份账号最小权限,备份文件访问要控权

- 监控告警 :备份失败、目录增长异常、磁盘水位必须告警

- 3-2-1 思路 :3 份副本、2 种介质、1 份异地(理念比工具更重要)

统一回复:需要脚本可以通过网盘下载链接:

「Mysql8.0备份脚本」链接:https://pan.quark.cn/s/87d77b13f201

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/26 19:58:18

Markdown写文档 + PyTorch-CUDA-v2.6做实验:科研最佳实践

Markdown写文档 PyTorch-CUDA-v2.6做实验&#xff1a;科研最佳实践 在深度学习研究日益复杂的今天&#xff0c;一个常见的场景是&#xff1a;你终于跑通了一个新模型&#xff0c;在自己的机器上取得了不错的结果。信心满满地把代码发给合作者时&#xff0c;对方却回复&#xf…

作者头像 李华
网站建设 2026/5/26 19:58:43

Dify平台接入PyTorch-CUDA-v2.6镜像实现可视化AI开发

Dify平台接入PyTorch-CUDA-v2.6镜像实现可视化AI开发 在当今AI模型日益复杂、训练任务愈发密集的背景下&#xff0c;一个能兼顾高效性与易用性的开发环境&#xff0c;几乎成了每个团队的刚需。想象一下这样的场景&#xff1a;新来的实习生第一天上班&#xff0c;不用再花三天时…

作者头像 李华
网站建设 2026/5/26 19:58:50

终极指南:用tsParticles打造惊艳网页粒子特效系统

终极指南&#xff1a;用tsParticles打造惊艳网页粒子特效系统 【免费下载链接】tsparticles 项目地址: https://gitcode.com/gh_mirrors/tsp/tsparticles 还在为网页设计缺乏动感和视觉冲击力而烦恼吗&#xff1f;传统的静态背景已经无法满足现代用户的审美需求。想要快…

作者头像 李华
网站建设 2026/5/26 19:58:18

Jetson设备深度学习推理性能优化实战:从入门到精通

Jetson设备深度学习推理性能优化实战&#xff1a;从入门到精通 【免费下载链接】jetson-inference jetson-inference: 提供了一个用于NVIDIA Jetson设备的深度学习推理和实时视觉DNN库&#xff0c;支持多种深度学习模型和应用。 项目地址: https://gitcode.com/gh_mirrors/je…

作者头像 李华
网站建设 2026/5/20 21:59:23

5分钟快速上手Vue3跨平台开发模板

5分钟快速上手Vue3跨平台开发模板 【免费下载链接】unibest 项目地址: https://gitcode.com/gh_mirrors/unib/unibest 还在为多端开发配置繁琐而头疼吗&#xff1f;unibest模板为您提供了一站式Vue3跨平台开发解决方案&#xff0c;让您专注于业务逻辑&#xff0c;轻松构…

作者头像 李华
网站建设 2026/5/20 22:44:10

告别论文焦虑:百考通AI如何用全流程智能辅导重塑学术写作体验

在学术研究的漫长征途上&#xff0c;论文写作无疑是每位研究者必须翻越的一座高山。从灵光一现的选题&#xff0c;到浩如烟海的文献&#xff0c;再到严谨枯燥的格式与反复修改的表述&#xff0c;每一个环节都充斥着挑战与焦虑。无论是初入科研殿堂的本科生&#xff0c;还是追求…

作者头像 李华