峰邑 发表于 昨天 21:15

MySQL备份恢复全攻略:mysqldump与xtrabackup实战(DBA救命指南)

对于DBA来说,数据库备份就像程序员的「Ctrl+S」——平时不起眼,出事了能救命,而且是最重要的救命手段,没有之一。
相信很多DBA都有过这样的惊魂时刻:开发手滑,一条没有WHERE条件的DELETE语句下去,用户表瞬间清空;服务器宕机,数据目录损坏,眼看业务停摆,领导在身后催得焦头烂额……
我至今还记得2018年的那次事故:开发误删用户全量数据,当时只有每天凌晨1次的全量备份,事故发生在下午5点,我们硬生生靠binlog恢复了4个小时,业务停摆半天,损失惨重。
从那以后,我们彻底重构了备份策略——从“裸奔式”每日全备,升级为「全量备份+增量备份+binlog归档」的黄金组合,RTO(恢复时间目标)从4小时压缩到30分钟,RPO(恢复点目标)从24小时缩减到5分钟,再也不用为数据丢失夜不能寐。
今天,就把这份沉淀了多年的MySQL备份恢复实战攻略,毫无保留分享给大家——涵盖mysqldump和xtrabackup两大核心工具,从基础用法到生产实战,从故障排查到最佳实践,新手能上手,老手能避坑。
一、先搞懂:两大备份工具,该选谁?

MySQL备份工具千千万,但生产环境最常用、最靠谱的,就两个:mysqldump 和 Percona XtraBackup。它们就像两把“双刃剑”,各有专攻,选对了能省90%的麻烦。
核心结论:小型数据库选mysqldump(简单省心),大型数据库选XtraBackup(高效低影响),跨版本迁移必用mysqldump。
1.1 mysqldump:MySQL官方“轻量选手”

mysqldump是MySQL自带的逻辑备份工具,说白了就是“把数据库里的数据,导出成SQL语句”,就像把一本书的内容逐字抄下来,可读性强,操作简单。
特性详细说明备份类型逻辑备份(导出SQL语句,肉眼可看)锁机制默认锁表,可用--single-transaction实现InnoDB一致性读(不锁表)速度较慢(要逐行查询、生成SQL),大数据量会“卡壳”适用场景小型数据库(50GB)、生产环境低影响备份优点快速、低影响、支持增量备份,恢复速度快缺点只支持同版本恢复,配置比mysqldump复杂,需要额外安装1.3 场景对应表(直接抄作业)

不用纠结,对照下面的场景选工具,准没错:
应用场景推荐工具备份策略开发测试环境mysqldump每日全备(简单省事)小型生产(500GB)XtraBackup每周全备 + 每日增量 + 流式备份跨版本迁移mysqldump逻辑导出导入(兼容性强)表级恢复mysqldump单表导出(精准恢复,不影响其他表)时间点恢复(PITR)XtraBackup + binlog全备 + binlog应用(恢复到任意时间点)1.4 环境要求(必看,避免踩坑)

环境不匹配,备份必失败!提前核对以下版本和配置:
组件版本要求关键说明MySQL Server8.0.35+ 或 8.4 LTS稳定版优先,避免使用测试版Percona XtraBackup8.0.35+版本必须与MySQL匹配(比如MySQL 8.0.35,XtraBackup也用8.0.35)操作系统Rocky 9 / Ubuntu 24.04生产环境推荐,兼容性最好磁盘空间数据量的2-3倍预留备份、压缩、恢复的空间,避免空间不足导致备份失败qpress最新版XtraBackup压缩备份必需工具二、实战操作:从准备到备份,一步到位

铺垫再多,不如动手操作。这部分是核心,每一步都有详细命令,直接复制粘贴就能用(记得替换自己的密码和目录)。
2.1 准备工作(3步搞定,避免后续踩坑)

2.1.1 安装备份工具

分两种操作系统,按需选择:
✅ Rocky Linux 9 / CentOS Stream 9

# 安装Percona仓库(XtraBackup需要)
sudo dnf install -y https://repo.percona.com/yum/percona-release-latest.noarch.rpm
sudo percona-release setup pxb-80

# 安装XtraBackup
sudo dnf install -y percona-xtrabackup-80

# 安装压缩工具(备份压缩必需)
sudo dnf install -y qpress lz4

# 验证安装(出现版本号即成功)
xtrabackup --version
# 正常输出:xtrabackup version 8.0.35-30 based on MySQL server 8.0.35✅ Ubuntu 24.04

sudo apt-get update
sudo apt-get install -y wget gnupg2 lsb-release
wget https://repo.percona.com/apt/percona-release_latest.generic_all.deb
sudo dpkg -i percona-release_latest.generic_all.deb
sudo percona-release setup pxb-80
sudo apt-get install -y percona-xtrabackup-80 qpress2.1.2 创建备份专用用户

不要用root用户备份!创建专用备份用户,分配最小权限,更安全:
-- 登录MySQL,执行以下SQL
CREATE USER 'backup'@'localhost' IDENTIFIED BY 'BackupPass@2024';-- 密码替换成自己的

-- mysqldump所需权限(复制粘贴即可)
GRANT SELECT, SHOW VIEW, TRIGGER, LOCK TABLES, EVENT ON *.* TO 'backup'@'localhost';

-- XtraBackup所需权限(复制粘贴即可)
GRANT RELOAD, PROCESS, LOCK TABLES, REPLICATION CLIENT ON *.* TO 'backup'@'localhost';
GRANT BACKUP_ADMIN ON *.* TO 'backup'@'localhost';-- MySQL 8.0+ 必需
GRANT SELECT ON performance_schema.log_status TO 'backup'@'localhost';
GRANT SELECT ON performance_schema.keyring_component_status TO 'backup'@'localhost';

FLUSH PRIVILEGES;-- 刷新权限,立即生效2.1.3 规划备份目录

目录混乱会导致后续恢复找不到文件,建议按以下结构创建,一目了然:
# 创建备份目录结构(一次性执行)
sudo mkdir -p /backup/mysql/{full,incremental,binlog,scripts,logs}
sudo chown -R mysql:mysql /backup/mysql# 授权mysql用户,避免权限不足
sudo chmod 750 /backup/mysql# 限制访问权限,更安全

# 目录说明(记好,后续用得到):
# /backup/mysql/full      - 存放全量备份
# /backup/mysql/incremental - 存放增量备份
# /backup/mysql/binlog      - 存放binlog归档
# /backup/mysql/scripts   - 存放备份脚本
# /backup/mysql/logs      - 存放备份日志

# 重要提醒:备份目录建议用独立磁盘或NFS挂载,避免和数据目录在同一磁盘(防止磁盘损坏一起丢)2.2 核心操作:mysqldump实战用法

适合小型数据库,操作简单,以下是最常用的几种场景,覆盖90%的需求:
✅ 备份单个数据库

mysqldump -u backup -p'BackupPass@2024' \
    --single-transaction \# InnoDB不锁表,关键参数
    --routines \            # 备份存储过程和函数
    --triggers \            # 备份触发器
    --events \            # 备份事件调度器
    mydb > /backup/mysql/full/mydb_$(date +%Y%m%d).sql# 备份到指定目录,带日期✅ 备份多个数据库

mysqldump -u backup -p'BackupPass@2024' \
    --single-transaction \
    --routines \
    --triggers \
    --databases db1 db2 db3 > /backup/mysql/full/multi_db_$(date +%Y%m%d).sql✅ 备份所有数据库(最常用)

mysqldump -u backup -p'BackupPass@2024' \
    --single-transaction \
    --routines \
    --triggers \
    --events \
    --all-databases > /backup/mysql/full/all_db_$(date +%Y%m%d).sql✅ 备份单个表(精准恢复,不影响其他表)

mysqldump -u backup -p'BackupPass@2024' \
    --single-transaction \
    mydb users > /backup/mysql/full/mydb_users_$(date +%Y%m%d).sql# mydb是库名,users是表名✅ 生产环境推荐参数组合(带压缩,更省空间)

这是我平时用的配置,兼顾安全和效率,直接复制用:
mysqldump -u backup -p'BackupPass@2024' \
    --single-transaction \          # InnoDB一致性读,不锁表(必需)
    --source-data=2 \               # 记录binlog位置(注释形式,方便恢复)
    --routines \                  # 包含存储过程和函数
    --triggers \                  # 包含触发器
    --events \                      # 包含事件调度器
    --set-gtid-purged=AUTO \      # GTID自动处理(MySQL 8.0+ 推荐)
    --hex-blob \                  # 二进制数据用十六进制,避免乱码
    --quick \                     # 逐行读取,减少内存占用
    --max-allowed-packet=512M \   # 支持大数据包,避免导出失败
    --default-character-set=utf8mb4 \# 字符集,避免乱码
    --all-databases \
    | gzip > /backup/mysql/full/all_db_$(date +%Y%m%d).sql.gz# 压缩备份,省空间注意:MySQL 8.0.26+ 版本,--master-data 参数已改为 --source-data,别用错了!
2.3 核心操作:XtraBackup实战用法

适合大型数据库,热备份、速度快,重点掌握全量和增量备份(生产环境最常用)。
✅ 全量备份(基础,增量备份的前提)

# 基础全量备份
xtrabackup --backup \
    --user=backup \
    --password='BackupPass@2024' \
    --target-dir=/backup/mysql/full/$(date +%Y%m%d)# 备份目录带日期,方便区分

# 备份并压缩(推荐,省空间)
xtrabackup --backup \
    --user=backup \
    --password='BackupPass@2024' \
    --target-dir=/backup/mysql/full/$(date +%Y%m%d) \
    --compress \                  # 开启压缩
    --compress-threads=4          # 4线程压缩,提升速度

# 流式备份到远程(大型库推荐,避免本地磁盘不够)
xtrabackup --backup \
    --user=backup \
    --password='BackupPass@2024' \
    --stream=xbstream \         # 流式输出
    --compress \
    | ssh backup@remote-server "cat > /backup/mysql/full_$(date +%Y%m%d).xbstream"# 传输到远程服务器✅ 增量备份(省空间,适合大型库)

增量备份只备份上次备份后变化的数据,比全量备份小很多,步骤如下(必须基于全量备份):
# 第一步:创建全量备份(基准备份,只做一次)
xtrabackup --backup \
    --user=backup \
    --password='BackupPass@2024' \
    --target-dir=/backup/mysql/full/base

# 第二步:创建第一次增量备份(基于全量备份)
xtrabackup --backup \
    --user=backup \
    --password='BackupPass@2024' \
    --target-dir=/backup/mysql/incremental/inc1 \
    --incremental-basedir=/backup/mysql/full/base# 指定基准备份目录

# 第三步:创建第二次增量备份(基于第一次增量)
xtrabackup --backup \
    --user=backup \
    --password='BackupPass@2024' \
    --target-dir=/backup/mysql/incremental/inc2 \
    --incremental-basedir=/backup/mysql/incremental/inc1# 指定上一次增量备份目录✅ 备份准备与恢复(关键,备份了不能恢复等于白做)

XtraBackup备份后不能直接恢复,必须先“准备”(应用redo log,确保数据一致性):
# 1. 准备全量备份(基准备份)
xtrabackup --prepare \
    --apply-log-only \# 只应用redo log,不做其他操作(增量备份必需)
    --target-dir=/backup/mysql/full/base

# 2. 应用第一次增量备份到基准备份
xtrabackup --prepare \
    --apply-log-only \
    --target-dir=/backup/mysql/full/base \
    --incremental-dir=/backup/mysql/incremental/inc1

# 3. 应用第二次增量备份(最后一次增量,不用加--apply-log-only)
xtrabackup --prepare \
    --target-dir=/backup/mysql/full/base \
    --incremental-dir=/backup/mysql/incremental/inc2

# 4. 最终准备(确保数据完整)
xtrabackup --prepare \
    --target-dir=/backup/mysql/full/base

# 恢复备份(停止MySQL后执行)
xtrabackup --copy-back \
    --target-dir=/backup/mysql/full/base

# 恢复后设置权限(必须,否则MySQL启动失败)
chown -R mysql:mysql /data/mysql/data# /data/mysql/data是你的MySQL数据目录三、生产实战:脚本+案例,直接落地

手动执行备份太麻烦,而且容易忘,生产环境一定要用脚本+定时任务,实现自动化备份。另外,附上3个高频故障恢复案例,帮你应对突发情况。
3.1 生产环境备份脚本(直接复制可用)

✅ mysqldump备份脚本(小型数据库)

文件路径:/backup/mysql/scripts/mysqldump_backup.sh,记得替换配置中的密码和目录:
#!/bin/bash
# 文件:/backup/mysql/scripts/mysqldump_backup.sh
# 功能:生产环境mysqldump自动化备份脚本
# 用法:./mysqldump_backup.sh

set -e# 执行出错立即退出

# 配置(按需修改)
MYSQL_USER="backup"
MYSQL_PASS="BackupPass@2024"# 替换成你的备份用户密码
MYSQL_HOST="localhost"
BACKUP_DIR="/backup/mysql/full"
LOG_DIR="/backup/mysql/logs"
RETENTION_DAYS=7# 备份保留7天,自动清理
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="${LOG_DIR}/mysqldump_${DATE}.log"

# 备份元数据库(记录备份信息,可选)
META_DB="backup_meta"
RECORD_BACKUP=true

# 初始化目录
mkdir -p ${BACKUP_DIR} ${LOG_DIR}

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

# 记录备份结果到元数据库
record_backup() {
    local status=$1
    local error_msg=$2
    local end_time=$(date '+%Y-%m-%d %H:%M:%S')
    local backup_size=$(stat -c%s ${BACKUP_FILE} 2>/dev/null || echo 0)
    local compressed_size=$(stat -c%s ${BACKUP_FILE}.gz 2>/dev/null || echo 0)

    if [ "$RECORD_BACKUP" = true ]; then
      mysql -u${MYSQL_USER} -p${MYSQL_PASS} ${META_DB} << EOF
UPDATE backup_history SET
    end_time = '${end_time}',
    duration_seconds = TIMESTAMPDIFF(SECOND, start_time, '${end_time}'),
    status = '${status}',
    backup_size_bytes = ${backup_size},
    compressed_size_bytes = ${compressed_size},
    error_message = '${error_msg}'
WHERE id = ${BACKUP_ID};
EOF
    fi
}

# 全量备份
do_full_backup() {
    log "Starting full backup..."
    BACKUP_FILE="${BACKUP_DIR}/all_databases_${DATE}.sql"

    # 获取binlog位置(用于时间点恢复)
    BINLOG_INFO=$(mysql -u${MYSQL_USER} -p${MYSQL_PASS} -N -e "SHOW MASTER STATUS")
    BINLOG_FILE=$(echo $BINLOG_INFO | awk '{print $1}')
    BINLOG_POS=$(echo $BINLOG_INFO | awk '{print $2}')
    GTID_EXECUTED=$(mysql -u${MYSQL_USER} -p${MYSQL_PASS} -N -e "SELECT @@global.gtid_executed" | tr '\n' ' ')

    # 记录备份开始
    if [ "$RECORD_BACKUP" = true ]; then
      BACKUP_ID=$(mysql -u${MYSQL_USER} -p${MYSQL_PASS} ${META_DB} -N -e "
            INSERT INTO backup_history (backup_type, backup_tool, backup_path, start_time, binlog_file, binlog_position, gtid_executed)
            VALUES ('full', 'mysqldump', '${BACKUP_FILE}', NOW(), '${BINLOG_FILE}', ${BINLOG_POS}, '${GTID_EXECUTED}');
            SELECT LAST_INSERT_ID();
      ")
    fi

    # 执行备份
    log "Backup file: ${BACKUP_FILE}"
    log "Binlog: ${BINLOG_FILE}:${BINLOG_POS}"

    mysqldump -u${MYSQL_USER} -p${MYSQL_PASS} -h${MYSQL_HOST} \
      --single-transaction \
      --source-data=2 \
      --routines \
      --triggers \
      --events \
      --set-gtid-purged=AUTO \
      --hex-blob \
      --quick \
      --max-allowed-packet=512M \
      --default-character-set=utf8mb4 \
      --all-databases > ${BACKUP_FILE} 2>>${LOG_FILE}

    if [ $? -eq 0 ]; then
      log "Backup completed successfully"
      # 压缩备份
      log "Compressing backup..."
      gzip ${BACKUP_FILE}
      log "Compressed size: $(du -h ${BACKUP_FILE}.gz | cut -f1)"
      record_backup "success" ""
    else
      log "Backup FAILED!"
      record_backup "failed" "mysqldump failed"
      exit 1
    fi
}

# 单库备份
do_single_backup() {
    local DB_NAME=$1
    log "Starting single database backup: ${DB_NAME}"
    BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}_${DATE}.sql"

    mysqldump -u${MYSQL_USER} -p${MYSQL_PASS} -h${MYSQL_HOST} \
      --single-transaction \
      --source-data=2 \
      --routines \
      --triggers \
      --set-gtid-purged=AUTO \
      --hex-blob \
      --quick \
      ${DB_NAME} > ${BACKUP_FILE} 2>>${LOG_FILE}

    if [ $? -eq 0 ]; then
      log "Single database backup completed: ${DB_NAME}"
      gzip ${BACKUP_FILE}
    else
      log "Single database backup FAILED: ${DB_NAME}"
      exit 1
    fi
}

# 清理旧备份(保留7天)
cleanup_old_backups() {
    log "Cleaning up backups older than ${RETENTION_DAYS} days..."
    find ${BACKUP_DIR} -name "*.sql.gz" -mtime +${RETENTION_DAYS} -delete
    find ${LOG_DIR} -name "*.log" -mtime +${RETENTION_DAYS} -delete
    log "Cleanup completed"
}

# 主程序
case "$1" in
    full)
      do_full_backup
      ;;
    single)
      if [ -z "$2" ]; then
            log "Error: Database name required"
            exit 1
      fi
      do_single_backup $2
      ;;
    *)
      do_full_backup# 默认执行全量备份
      ;;
esac

cleanup_old_backups
log "Backup script completed"✅ XtraBackup备份脚本(大型数据库)

文件路径:/backup/mysql/scripts/xtrabackup_backup.sh,支持全量和增量备份:
#!/bin/bash
# 文件:/backup/mysql/scripts/xtrabackup_backup.sh
# 功能:生产环境XtraBackup自动化备份脚本
# 用法:./xtrabackup_backup.sh

set -e

# 配置(按需修改)
MYSQL_USER="backup"
MYSQL_PASS="BackupPass@2024"
BACKUP_BASE="/backup/mysql"
FULL_BACKUP_DIR="${BACKUP_BASE}/full"
INCR_BACKUP_DIR="${BACKUP_BASE}/incremental"
LOG_DIR="${BACKUP_BASE}/logs"
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="${LOG_DIR}/xtrabackup_${DATE}.log"
COMPRESS_THREADS=4# 压缩线程数
PARALLEL_THREADS=4# 并行备份线程数

# 保留策略
FULL_RETENTION_DAYS=30# 全量备份保留30天
INCR_RETENTION_DAYS=7   # 增量备份保留7天

# 初始化目录
mkdir -p ${FULL_BACKUP_DIR} ${INCR_BACKUP_DIR} ${LOG_DIR}

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

# 获取最新全量备份
get_latest_full_backup() {
    ls -td ${FULL_BACKUP_DIR}/*/ 2>/dev/null | head -1
}

# 获取最新增量备份
get_latest_incremental() {
    local base_dir=$1
    local base_name=$(basename $base_dir)
    ls -td ${INCR_BACKUP_DIR}/${base_name}_inc*/ 2>/dev/null | head -1
}

# 全量备份
do_full_backup() {
    log "Starting full backup..."
    local BACKUP_PATH="${FULL_BACKUP_DIR}/${DATE}"

    xtrabackup --backup \
      --user=${MYSQL_USER} \
      --password=${MYSQL_PASS} \
      --target-dir=${BACKUP_PATH} \
      --parallel=${PARALLEL_THREADS} \
      --compress \
      --compress-threads=${COMPRESS_THREADS} \
      2>>${LOG_FILE}

    if [ $? -eq 0 ]; then
      log "Full backup completed: ${BACKUP_PATH}"
      cat ${BACKUP_PATH}/xtrabackup_checkpoints | tee -a ${LOG_FILE}
      local SIZE=$(du -sh ${BACKUP_PATH} | cut -f1)
      log "Backup size: ${SIZE}"
    else
      log "Full backup FAILED!"
      exit 1
    fi
}

# 增量备份
do_incremental_backup() {
    log "Starting incremental backup..."

    # 获取基准目录(没有全量备份则自动执行全量)
    local LATEST_FULL=$(get_latest_full_backup)
    if [ -z "$LATEST_FULL" ]; then
      log "No full backup found, performing full backup instead"
      do_full_backup
      return
    fi

    local FULL_NAME=$(basename $LATEST_FULL)
    log "Base full backup: ${LATEST_FULL}"

    # 检查是否有之前的增量备份
    local LATEST_INCR=$(get_latest_incremental $LATEST_FULL)
    local BASE_DIR=$LATEST_FULL
    if [ -n "$LATEST_INCR" ]; then
      BASE_DIR=$LATEST_INCR
      log "Base incremental backup: ${LATEST_INCR}"
    fi

    # 生成增量备份名称
    local INCR_COUNT=$(ls -d ${INCR_BACKUP_DIR}/${FULL_NAME}_inc*/ 2>/dev/null | wc -l)
    local INCR_NAME="${FULL_NAME}_inc$((INCR_COUNT + 1))_${DATE}"
    local BACKUP_PATH="${INCR_BACKUP_DIR}/${INCR_NAME}"

    xtrabackup --backup \
      --user=${MYSQL_USER} \
      --password=${MYSQL_PASS} \
      --target-dir=${BACKUP_PATH} \
      --incremental-basedir=${BASE_DIR} \
      --parallel=${PARALLEL_THREADS} \
      --compress \
      --compress-threads=${COMPRESS_THREADS} \
      2>>${LOG_FILE}

    if [ $? -eq 0 ]; then
      log "Incremental backup completed: ${BACKUP_PATH}"
      cat ${BACKUP_PATH}/xtrabackup_checkpoints | tee -a ${LOG_FILE}
    else
      log "Incremental backup FAILED!"
      exit 1
    fi
}

# 清理旧备份
cleanup_old_backups() {
    log "Cleaning up old backups..."
    # 清理旧全量备份
    find ${FULL_BACKUP_DIR} -maxdepth 1 -type d -mtime +${FULL_RETENTION_DAYS} -exec rm -rf {} \;
    # 清理旧增量备份
    find ${INCR_BACKUP_DIR} -maxdepth 1 -type d -mtime +${INCR_RETENTION_DAYS} -exec rm -rf {} \;
    # 清理旧日志
    find ${LOG_DIR} -name "*.log" -mtime +${FULL_RETENTION_DAYS} -delete
    log "Cleanup completed"
}

# 主程序
case "$1" in
    full)
      do_full_backup
      ;;
    incremental|incr)
      do_incremental_backup
      ;;
    *)
      log "Usage: $0 "
      exit 1
      ;;
esac

cleanup_old_backups
log "Backup script completed"✅ 定时任务配置(自动化备份关键)

用crontab设置定时任务,让脚本自动执行,不用手动操作:
# 编辑crontab(执行命令)
crontab -e

# -------------- 小型数据库(mysqldump)--------------
# 每天凌晨2点执行全备,日志写入cron.log
0 2 * * * /backup/mysql/scripts/mysqldump_backup.sh full >> /backup/mysql/logs/cron.log 2>&1

# -------------- 大型数据库(XtraBackup)--------------
# 每周日凌晨2点执行全备
0 2 * * 0 /backup/mysql/scripts/xtrabackup_backup.sh full >> /backup/mysql/logs/cron.log 2>&1
# 周一到周六凌晨2点执行增量备份
0 2 * * 1-6 /backup/mysql/scripts/xtrabackup_backup.sh incremental >> /backup/mysql/logs/cron.log 2>&1

# -------------- binlog归档(每小时一次)--------------
0 * * * * /backup/mysql/scripts/binlog_archive.sh >> /backup/mysql/logs/cron.log 2>&13.2 高频故障恢复案例(实战必备)

案例一:误删数据恢复(最常见)

场景:开发误执行DELETE语句,删除了users表中当天的数据(本想删除无效用户,忘了加WHERE条件)。
-- 误操作SQL(噩梦开始)
DELETE FROM users WHERE created_at >= '2024-01-15';
-- 正确SQL(应该加条件)
DELETE FROM users WHERE created_at >= '2024-01-15' AND status = 'inactive';恢复步骤(关键:立即记录binlog位置,避免数据覆盖):
# 第一步:立即记录当前binlog位置(重中之重,防止binlog滚动)
mysql -uroot -p -e "SHOW MASTER STATUS"
# 记录结果:File: mysql-bin.000150, Position: 123456789

# 第二步:找到最近的全量备份
ls -la /backup/mysql/full/
# 假设最新备份:all_databases_20240115_020000.sql.gz

# 第三步:解压备份,恢复到临时库(避免影响生产库)
# 解压备份
gunzip -c /backup/mysql/full/all_databases_20240115_020000.sql.gz > /tmp/restore.sql

# 创建临时库
mysql -uroot -p -e "CREATE DATABASE users_recovery"

# 只恢复users表(精准恢复,节省时间)
# 方法1:用sed提取users表
sed -n '/^-- Table structure for table `users`/,/^-- Table structure for table/p' /tmp/restore.sql > /tmp/users_table.sql

# 方法2:用awk提取(更精准)
awk '/^-- Table structure for table `users`/{f=1} f; /^UNLOCK TABLES;/{if(f) exit}' /tmp/restore.sql > /tmp/users_table.sql

# 导入到临时库
mysql -uroot -p users_recovery < /tmp/users_table.sql

# 第四步:确认备份的binlog位置(从备份文件中提取)
grep "CHANGE MASTER TO" /tmp/restore.sql | head -1
# 输出:-- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000145', MASTER_LOG_POS=789012;

# 第五步:应用binlog,恢复到误删之前
# 先找到误删操作的binlog位置
mysqlbinlog /data/mysql/binlog/mysql-bin.000145 mysql-bin.000146 \
    --start-position=789012 \
    --database=mydb \
    -v | grep -B5 -A5 "DELETE FROM.*users"

# 找到误删位置后,应用binlog到误删前
mysqlbinlog /data/mysql/binlog/mysql-bin.000145 mysql-bin.000146 \
    --start-position=789012 \
    --stop-position=误删操作位置 \# 替换成实际误删位置
    --database=mydb | mysql -uroot -p users_recovery

# 第六步:将恢复的数据导回生产库
mysqldump -uroot -p users_recovery users | mysql -uroot -p mydb案例二:时间点恢复(PITR)

场景:数据库因误操作、病毒攻击等,需要恢复到指定时间点(比如2024-01-15 14:30:00)。
#!/bin/bash
# 文件:point_in_time_recovery.sh
# 功能:MySQL时间点恢复脚本
# 用法:直接执行,修改配置中的恢复时间和目录

RECOVERY_TIME="2024-01-15 14:30:00"# 目标恢复时间
BACKUP_DIR="/backup/mysql/full/20240115"# 最近的全量备份目录
BINLOG_DIR="/data/mysql/binlog"# binlog目录
RECOVERY_DIR="/data/mysql_recovery"# 恢复临时目录

# 第一步:准备备份(解压+应用redo log)
echo "Preparing backup..."
cp -r ${BACKUP_DIR} ${RECOVERY_DIR}

# 解压压缩的备份文件(如果是压缩备份)
cd ${RECOVERY_DIR}
for f in *.qp; do
    qpress -d $f ./
done

# 准备备份,确保数据一致性
xtrabackup --prepare --target-dir=${RECOVERY_DIR}

# 第二步:获取备份的binlog位置(从备份文件中提取)
BINLOG_INFO=$(cat ${RECOVERY_DIR}/xtrabackup_binlog_info)
BINLOG_FILE=$(echo $BINLOG_INFO | awk '{print $1}')
BINLOG_POS=$(echo $BINLOG_INFO | awk '{print $2}')

echo "Backup binlog position: ${BINLOG_FILE}:${BINLOG_POS}"

# 第三步:找到需要应用的binlog文件
BINLOG_FILES=$(ls ${BINLOG_DIR}/mysql-bin.* | sort | awk -v start="${BINLOG_FILE}" '$0 >= start')

# 第四步:应用binlog到指定时间点
echo "Applying binlog until ${RECOVERY_TIME}..."
mysqlbinlog ${BINLOG_FILES} \
    --start-position=${BINLOG_POS} \
    --stop-datetime="${RECOVERY_TIME}" \
    > ${RECOVERY_DIR}/binlog_recovery.sql

# 第五步:恢复数据到生产环境
echo "Stopping MySQL..."
systemctl stop mysqld

# 备份当前生产数据目录(安全起见,防止恢复失败)
mv /data/mysql/data /data/mysql/data_old_$(date +%Y%m%d_%H%M%S)

# 恢复备份
xtrabackup --copy-back --target-dir=${RECOVERY_DIR}
chown -R mysql:mysql /data/mysql/data# 设置权限

# 启动MySQL
systemctl start mysqld

# 应用binlog增量,完成时间点恢复
echo "Applying binlog recovery..."
mysql -uroot -p < ${RECOVERY_DIR}/binlog_recovery.sql

echo "Point-in-time recovery completed to ${RECOVERY_TIME}"案例三:大表备份与恢复(500GB+)

场景:orders表有5亿条数据,单表500GB,直接备份/恢复会卡死,用分区导出导入,提升效率。
# 备份脚本:large_table_backup.sh(分区导出)#!/bin/bashTABLE="orders"DB="ecommerce"BACKUP_DIR="/backup/mysql/large_tables"DATE=$(date +%Y%m%d)PARALLEL_JOBS=4# 4线程并行,按需调整mkdir -p ${BACKUP_DIR}/${DATE}# 获取表的主键范围(按主键分区)MAX_ID=$(mysql -ubackup -p'BackupPass@2024' -N -e "SELECT MAX(id) FROM ${DB}.${TABLE}")BATCH_SIZE=$((MAX_ID / PARALLEL_JOBS))# 并行导出(按主键分段,避免单线程卡死)for i in $(seq 1 ${PARALLEL_JOBS}); do    START_ID=$(( (i-1) * BATCH_SIZE + 1 ))    END_ID=$(( i * BATCH_SIZE ))    if [ $i -eq ${PARALLEL_JOBS} ]; then      END_ID=${MAX_ID}# 最后一段包含剩余数据    fi    echo "Exporting ${TABLE} rows ${START_ID} to ${END_ID}..."    mysqldump -ubackup -p'BackupPass@2024' \      --single-transaction \      --no-create-info \# 只导出数据,表结构单独导出      --where="id >= ${START_ID} AND id
页: [1]
查看完整版本: MySQL备份恢复全攻略:mysqldump与xtrabackup实战(DBA救命指南)