MySQL数据库备份恢复策略:全量、增量与binlog应用

环境要求

组件 版本要求 说明
MySQL 5.7+/8.0+ 支持GTID和增强的binlog功能
mysqldump 与MySQL版本匹配 逻辑备份工具,MySQL自带
Percona XtraBackup 8.0+ 物理备份工具,支持热备份
存储空间 数据库大小的3-5倍 用于存储备份文件
权限要求 SUPER、RELOAD、REPLICATION权限 备份和恢复需要的权限

二、详细步骤

2.1 准备工作

2.1.1 检查MySQL配置

确保MySQL已启用binlog,这是增量备份和时间点恢复的基础。

复制代码
# 登录MySQL
mysql -u root -p

# 检查binlog是否启用
SHOW VARIABLES LIKE 'log_bin';
# 输出:log_bin | ON

# 查看binlog格式
SHOW VARIABLES LIKE 'binlog_format';
# 推荐使用ROW格式

# 查看binlog文件列表
SHOW BINARY LOGS;

配置binlog(如果未启用):

复制代码
# 编辑MySQL配置文件
sudo vim /etc/mysql/my.cnf

# 添加以下配置
[mysqld]
server-id = 1
log_bin = /var/log/mysql/mysql-bin
binlog_format = ROW
expire_logs_days = 7
max_binlog_size = 100M

# 重启MySQL
sudo systemctl restart mysql
2.1.2 创建备份用户

为备份操作创建专用用户,遵循最小权限原则。

复制代码
-- 创建备份用户
CREATE USER 'backup'@'localhost' IDENTIFIED BY 'StrongPassword123!';

-- 授予必要权限
GRANT SELECT, RELOAD, LOCK TABLES, REPLICATION CLIENT, SHOW VIEW, EVENT, TRIGGER ON *.* TO 'backup'@'localhost';

-- 刷新权限
FLUSH PRIVILEGES;
2.1.3 准备备份目录
复制代码
# 创建备份目录
sudo mkdir -p /backup/mysql/{full,incremental,binlog}

# 设置权限
sudo chown -R mysql:mysql /backup/mysql
sudo chmod 750 /backup/mysql

# 创建备份日志目录
sudo mkdir -p /var/log/mysql_backup

2.2 全量备份

2.2.1 使用mysqldump进行逻辑备份

mysqldump是MySQL自带的逻辑备份工具,适合中小型数据库。

基础全量备份

复制代码
# 备份所有数据库
mysqldump -u backup -p --all-databases \
  --single-transaction \
  --flush-logs \
  --master-data=2 \
  --routines \
  --triggers \
  --events \
  > /backup/mysql/full/all_databases_$(date +%Y%m%d_%H%M%S).sql

# 压缩备份文件
gzip /backup/mysql/full/all_databases_*.sql

参数说明

  • --single-transaction:对InnoDB表使用一致性快照,不锁表

  • --flush-logs:备份前刷新binlog,生成新的binlog文件

  • --master-data=2:记录binlog位置,用于增量恢复

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

  • --triggers:备份触发器

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

备份单个数据库

复制代码
# 备份指定数据库
mysqldump -u backup -p \
  --single-transaction \
  --master-data=2 \
  --databases ecommerce \
  > /backup/mysql/full/ecommerce_$(date +%Y%m%d).sql
2.2.2 使用XtraBackup进行物理备份

XtraBackup适合大型数据库,支持热备份,备份和恢复速度快。

安装XtraBackup

复制代码
# Ubuntu/Debian
wget https://downloads.percona.com/downloads/Percona-XtraBackup-LATEST/Percona-XtraBackup-8.0/binary/debian/focal/x86_64/percona-xtrabackup-80_8.0.35-30-1.focal_amd64.deb
sudo dpkg -i percona-xtrabackup-80_8.0.35-30-1.focal_amd64.deb

# CentOS/RHEL
sudo yum install https://repo.percona.com/yum/percona-release-latest.noarch.rpm
sudo yum install percona-xtrabackup-80

执行物理备份

复制代码
# 全量备份
xtrabackup --backup \
  --user=backup \
  --password='StrongPassword123!' \
  --target-dir=/backup/mysql/full/xtrabackup_$(date +%Y%m%d)

# 压缩备份(可选)
xtrabackup --backup \
  --user=backup \
  --password='StrongPassword123!' \
  --compress \
  --compress-threads=4 \
  --target-dir=/backup/mysql/full/xtrabackup_compressed_$(date +%Y%m%d)

2.3 增量备份与binlog管理

2.3.1 binlog备份

binlog记录了所有数据变更,是增量备份的核心。

手动备份binlog

复制代码
# 查看当前binlog文件
mysql -u root -p -e "SHOW MASTER STATUS;"

# 刷新binlog,生成新文件
mysql -u root -p -e "FLUSH LOGS;"

# 复制binlog到备份目录
cp /var/log/mysql/mysql-bin.* /backup/mysql/binlog/

自动备份binlog脚本

复制代码
#!/bin/bash
# 文件名:backup_binlog.sh
# 功能:自动备份MySQL binlog

BACKUP_DIR="/backup/mysql/binlog"
MYSQL_BINLOG_DIR="/var/log/mysql"
DATE=$(date +%Y%m%d)

# 刷新binlog
mysql -u root -p'password' -e "FLUSH LOGS;"

# 获取当前binlog文件
CURRENT_BINLOG=$(mysql -u root -p'password' -e "SHOW MASTER STATUS\G" | grep File | awk '{print $2}')

# 备份除当前文件外的所有binlog
for binlog in $(ls $MYSQL_BINLOG_DIR/mysql-bin.[0-9]*); do
    filename=$(basename $binlog)
    if [ "$filename" != "$CURRENT_BINLOG" ]; then
        cp $binlog $BACKUP_DIR/${filename}_${DATE}
    fi
done

# 清理7天前的备份
find $BACKUP_DIR -name "mysql-bin.*" -mtime +7 -delete

echo "Binlog backup completed at $(date)"
2.3.2 时间点恢复准备

记录关键时间点的binlog位置,便于精确恢复。

复制代码
-- 查看当前binlog位置
SHOW MASTER STATUS;

-- 查看binlog事件
SHOW BINLOG EVENTS IN 'mysql-bin.000001' LIMIT 10;

-- 使用mysqlbinlog查看binlog内容
mysqlbinlog /var/log/mysql/mysql-bin.000001 | less

2.4 数据恢复

2.4.1 从mysqldump备份恢复

完全恢复

复制代码
# 解压备份文件
gunzip /backup/mysql/full/all_databases_20240125.sql.gz

# 恢复所有数据库
mysql -u root -p < /backup/mysql/full/all_databases_20240125.sql

# 恢复单个数据库
mysql -u root -p ecommerce < /backup/mysql/full/ecommerce_20240125.sql

时间点恢复

复制代码
# 1. 先恢复全量备份
mysql -u root -p < /backup/mysql/full/all_databases_20240125.sql

# 2. 查看备份文件中记录的binlog位置
head -30 /backup/mysql/full/all_databases_20240125.sql | grep "CHANGE MASTER"
# 输出:CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000010', MASTER_LOG_POS=154;

# 3. 应用binlog到指定时间点(假设误操作发生在10:30)
mysqlbinlog --start-position=154 \
  --stop-datetime="2024-01-25 10:29:59" \
  /var/log/mysql/mysql-bin.000010 \
  /var/log/mysql/mysql-bin.000011 \
  | mysql -u root -p
2.4.2 从XtraBackup备份恢复

准备备份

复制代码
# 1. 准备备份(应用事务日志)
xtrabackup --prepare \
  --target-dir=/backup/mysql/full/xtrabackup_20240125

# 如果是压缩备份,先解压
xtrabackup --decompress \
  --target-dir=/backup/mysql/full/xtrabackup_compressed_20240125

恢复数据

复制代码
# 1. 停止MySQL服务
sudo systemctl stop mysql

# 2. 清空数据目录(谨慎操作)
sudo rm -rf /var/lib/mysql/*

# 3. 恢复数据
xtrabackup --copy-back \
  --target-dir=/backup/mysql/full/xtrabackup_20240125

# 4. 修复权限
sudo chown -R mysql:mysql /var/lib/mysql

# 5. 启动MySQL
sudo systemctl start mysql

三、示例代码和配置

3.1 自动化备份脚本

复制代码
#!/bin/bash
# 文件名:mysql_backup.sh
# 功能:MySQL自动化备份脚本(全量+增量)

set -e

# 配置变量
MYSQL_USER="backup"
MYSQL_PASS="StrongPassword123!"
BACKUP_DIR="/backup/mysql"
RETENTION_DAYS=30
LOG_FILE="/var/log/mysql_backup/backup.log"

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

# 全量备份函数
full_backup() {
    log "Starting full backup..."

    BACKUP_FILE="$BACKUP_DIR/full/full_backup_$(date +%Y%m%d_%H%M%S).sql"

    mysqldump -u $MYSQL_USER -p$MYSQL_PASS \
        --all-databases \
        --single-transaction \
        --flush-logs \
        --master-data=2 \
        --routines \
        --triggers \
        --events \
        > $BACKUP_FILE

    # 压缩备份
    gzip $BACKUP_FILE

    log "Full backup completed: ${BACKUP_FILE}.gz"
}

# 增量备份(binlog)函数
incremental_backup() {
    log "Starting incremental backup (binlog)..."

    # 刷新binlog
    mysql -u $MYSQL_USER -p$MYSQL_PASS -e "FLUSH LOGS;"

    # 获取当前binlog
    CURRENT_BINLOG=$(mysql -u $MYSQL_USER -p$MYSQL_PASS -e "SHOW MASTER STATUS\G" | grep File | awk '{print $2}')

    # 备份binlog
    for binlog in /var/log/mysql/mysql-bin.[0-9]*; do
        filename=$(basename $binlog)
        if [ "$filename" != "$CURRENT_BINLOG" ]; then
            cp $binlog $BACKUP_DIR/binlog/
        fi
    done

    log "Incremental backup completed"
}

# 清理旧备份
cleanup_old_backups() {
    log "Cleaning up old backups..."

    find $BACKUP_DIR/full -name "*.sql.gz" -mtime +$RETENTION_DAYS -delete
    find $BACKUP_DIR/binlog -name "mysql-bin.*" -mtime +$RETENTION_DAYS -delete

    log "Cleanup completed"
}

# 验证备份
verify_backup() {
    LATEST_BACKUP=$(ls -t $BACKUP_DIR/full/*.sql.gz | head -1)

    if [ -f "$LATEST_BACKUP" ]; then
        SIZE=$(du -h $LATEST_BACKUP | awk '{print $1}')
        log "Latest backup: $LATEST_BACKUP (Size: $SIZE)"
    else
        log "ERROR: No backup file found!"
        exit 1
    fi
}

# 主流程
main() {
    log "========== MySQL Backup Started =========="

    # 判断是否执行全量备份(每天凌晨2点)
    HOUR=$(date +%H)
    if [ "$HOUR" == "02" ]; then
        full_backup
    fi

    # 增量备份(每小时执行)
    incremental_backup

    # 清理旧备份
    cleanup_old_backups

    # 验证备份
    verify_backup

    log "========== MySQL Backup Completed =========="
}

main

配置cron定时任务

复制代码
# 编辑crontab
crontab -e

# 每小时执行一次备份
0 * * * * /usr/local/bin/mysql_backup.sh

# 或者每天凌晨2点执行全量备份
0 2 * * * /usr/local/bin/mysql_backup.sh

3.2 实际应用案例

案例一:电商平台误删除订单数据恢复

场景描述:某电商平台运营人员在2024年1月25日上午10:28误执行了DELETE语句,删除了orders表中近一个月的数据,涉及5万多条订单记录。

恢复流程

  1. 立即停止应用写入

    停止应用服务,防止继续写入

    sudo systemctl stop tomcat

  2. 确定恢复时间点

    查看最近的全量备份

    ls -lh /backup/mysql/full/

    找到:full_backup_20240125_020000.sql.gz(凌晨2点的备份)

    确认误操作时间:10:28

    恢复目标时间:10:27(误操作前1分钟)

  3. 在测试环境恢复验证

    1. 恢复全量备份到测试库

    gunzip -c /backup/mysql/full/full_backup_20240125_020000.sql.gz | mysql -u root -p test_db

    2. 查看备份中的binlog位置

    head -30 /backup/mysql/full/full_backup_20240125_020000.sql | grep "CHANGE MASTER"

    输出:MASTER_LOG_FILE='mysql-bin.000015', MASTER_LOG_POS=154

    3. 应用binlog到10:27

    mysqlbinlog --start-position=154
    --stop-datetime="2024-01-25 10:27:00"
    /backup/mysql/binlog/mysql-bin.000015
    /backup/mysql/binlog/mysql-bin.000016
    | mysql -u root -p test_db

    4. 验证数据

    mysql -u root -p test_db -e "SELECT COUNT(*) FROM orders WHERE create_time >= '2024-01-01';"

    确认数据完整

  4. 生产环境恢复

    在生产环境执行相同的恢复流程

    恢复完成后重启应用

    sudo systemctl start tomcat

恢复结果

  • 成功恢复5万多条订单数据

  • 数据完整性100%

  • 业务中断时间:45分钟

  • 经验教训:增加了操作审核机制,重要操作需要二次确认


四、最佳实践和注意事项

4.1 最佳实践

4.1.1 备份策略设计

3-2-1备份原则

  • 至少保留3份备份

  • 使用2种不同的存储介质(本地磁盘+云存储)

  • 至少1份备份存放在异地

备份频率建议

复制代码
# 小型数据库(<10GB)
- 全量备份:每天1次
- binlog备份:每小时1次
- 保留周期:30天

# 中型数据库(10-100GB)
- 全量备份:每周1次
- 增量备份:每天1次
- binlog备份:每小时1次
- 保留周期:60天

# 大型数据库(>100GB)
- 全量备份:每月1次
- 增量备份:每周1次
- binlog备份:实时同步
- 保留周期:90天
4.1.2 备份验证

定期恢复演练

复制代码
#!/bin/bash
# 文件名:backup_verify.sh
# 功能:备份验证脚本

BACKUP_FILE="/backup/mysql/full/full_backup_20240125.sql.gz"
TEST_DB="backup_test"

# 1. 创建测试数据库
mysql -u root -p -e "DROP DATABASE IF EXISTS $TEST_DB; CREATE DATABASE $TEST_DB;"

# 2. 恢复备份到测试库
gunzip -c $BACKUP_FILE | mysql -u root -p $TEST_DB

# 3. 验证数据完整性
TABLE_COUNT=$(mysql -u root -p -N -e "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='$TEST_DB';")

if [ $TABLE_COUNT -gt 0 ]; then
    echo "Backup verification PASSED: $TABLE_COUNT tables restored"
else
    echo "Backup verification FAILED: No tables found"
    exit 1
fi

# 4. 清理测试库
mysql -u root -p -e "DROP DATABASE $TEST_DB;"

五、故障排查和监控

5.1 备份监控

监控备份任务执行状态

复制代码
# 检查最近的备份时间
ls -lht /backup/mysql/full/ | head -5

# 检查备份日志
tail -50 /var/log/mysql_backup/backup.log

# 检查备份文件大小变化
du -sh /backup/mysql/full/* | tail -10

备份告警脚本

复制代码
#!/bin/bash
# 文件名:backup_alert.sh
# 功能:备份监控告警

BACKUP_DIR="/backup/mysql/full"
ALERT_EMAIL="admin@example.com"

# 检查最近24小时是否有新备份
LATEST_BACKUP=$(find $BACKUP_DIR -name "*.sql.gz" -mtime -1 | wc -l)

if [ $LATEST_BACKUP -eq 0 ]; then
    echo "WARNING: No backup found in last 24 hours!" | mail -s "MySQL Backup Alert" $ALERT_EMAIL
fi

# 检查备份文件大小
LATEST_FILE=$(ls -t $BACKUP_DIR/*.sql.gz | head -1)
SIZE=$(stat -f%z "$LATEST_FILE" 2>/dev/null || stat -c%s "$LATEST_FILE")

if [ $SIZE -lt 1000000 ]; then
    echo "WARNING: Backup file too small: $SIZE bytes" | mail -s "MySQL Backup Alert" $ALERT_EMAIL
fi

附录

A. 备份恢复命令速查

复制代码
# mysqldump备份
mysqldump -u backup -p --all-databases --single-transaction --master-data=2 > backup.sql

# mysqldump恢复
mysql -u root -p < backup.sql

# XtraBackup备份
xtrabackup --backup --target-dir=/backup/full

# XtraBackup恢复
xtrabackup --prepare --target-dir=/backup/full
xtrabackup --copy-back --target-dir=/backup/full

# binlog查看
mysqlbinlog mysql-bin.000001

# binlog应用
mysqlbinlog --start-position=154 mysql-bin.000001 | mysql -u root -p

# 查看binlog位置
SHOW MASTER STATUS;
SHOW BINARY LOGS;

MySQL备份恢复全攻略:mysqldump与xtrabackup实战

一、概述

1.1 背景介绍

数据库备份是DBA工作中最重要的任务之一,没有之一。

2018年某次事故至今记忆犹新:开发人员误执行了一条没有WHERE条件的DELETE语句,直接删除了用户表的全部数据。当时的备份策略只有每天凌晨一次全量备份,而事故发生在下午5点。最终通过binlog恢复数据,但整个过程耗时4小时,造成了严重的业务影响。

从那之后,备份策略从简单的每日全备,升级为"全量备份+增量备份+binlog归档"的完整方案,RTO(恢复时间目标)从4小时降低到30分钟,RPO(恢复点目标)从24小时降低到5分钟。

MySQL备份主要有两大工具:

  • mysqldump:官方逻辑备份工具,简单可靠,适合小型数据库

  • Percona XtraBackup:物理热备份工具,高效快速,适合大型数据库

本文将详细介绍这两种工具的使用方法、适用场景以及生产环境的最佳实践。

1.2 技术特点

mysqldump特点

特性 说明
备份类型 逻辑备份(导出SQL语句)
锁机制 默认锁表,可用--single-transaction实现一致性读
备份速度 较慢,需要查询所有数据
恢复速度 较慢,需要执行SQL语句
适用场景 小型数据库(<50GB)、跨版本迁移、表级恢复
优点 简单、可读、跨平台、跨版本
缺点 大数据量时速度慢

Percona XtraBackup特点

特性 说明
备份类型 物理备份(复制数据文件)
锁机制 几乎无锁(InnoDB热备)
备份速度 快,直接复制文件
恢复速度 快,直接替换数据目录
适用场景 大型数据库(>50GB)、要求低影响
优点 快速、低影响、支持增量备份
缺点 只支持同版本恢复、配置较复杂

1.3 适用场景

场景 推荐工具 备份策略
开发测试环境 mysqldump 每日全备
小型生产(<50GB) mysqldump 每日全备 + binlog
中型生产(50-500GB) XtraBackup 每周全备 + 每日增量
大型生产(>500GB) XtraBackup 每周全备 + 每日增量 + 流式备份
跨版本迁移 mysqldump 逻辑导出导入
表级恢复 mysqldump 单表导出
时间点恢复(PITR) XtraBackup + binlog 全备 + binlog应用

1.4 环境要求

组件 版本要求 说明
MySQL Server 8.0.35+ 或 8.4 LTS
Percona XtraBackup 8.0.35+ 版本需与MySQL匹配
操作系统 Rocky 9 / Ubuntu 24.04
磁盘空间 数据量的2-3倍 备份+压缩+恢复空间
qpress 最新版 XtraBackup压缩支持

二、详细步骤

2.1 准备工作

2.1.1 安装备份工具
复制代码
# Rocky Linux 9 / CentOS Stream 9
# 安装Percona仓库
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 qpress
2.1.2 创建备份用户
复制代码
-- 创建备份专用用户
CREATEUSER'backup'@'localhost'IDENTIFIEDBY'BackupPass@2024';

-- mysqldump所需权限
GRANTSELECT, SHOWVIEW, TRIGGER, LOCKTABLES, EVENTON *.* TO'backup'@'localhost';

-- XtraBackup所需权限
GRANT RELOAD, PROCESS, LOCKTABLES, REPLICATIONCLIENTON *.* TO'backup'@'localhost';
GRANT BACKUP_ADMIN ON *.* TO'backup'@'localhost';  -- MySQL 8.0+
GRANTSELECTON performance_schema.log_status TO'backup'@'localhost';
GRANTSELECTON performance_schema.keyring_component_status TO'backup'@'localhost';

FLUSHPRIVILEGES;
2.1.3 规划备份目录
复制代码
# 创建备份目录结构
sudo mkdir -p /backup/mysql/{full,incremental,binlog,scripts,logs}
sudo chown -R mysql:mysql /backup/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 核心配置

2.2.1 mysqldump基础用法
复制代码
# 备份单个数据库
mysqldump -u backup -p'BackupPass@2024' \
    --single-transaction \
    --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

# 只备份表结构
mysqldump -u backup -p'BackupPass@2024' \
    --no-data \
    mydb > /backup/mysql/full/mydb_schema_$(date +%Y%m%d).sql

# 只备份数据
mysqldump -u backup -p'BackupPass@2024' \
    --no-create-info \
    --single-transaction \
    mydb > /backup/mysql/full/mydb_data_$(date +%Y%m%d).sql
2.2.2 mysqldump关键参数详解
复制代码
# 生产环境推荐参数组合
mysqldump -u backup -p'BackupPass@2024' \
    --single-transaction \          # InnoDB一致性读,不锁表
    --master-data=2 \               # 记录binlog位置(注释形式)
    --routines \                    # 包含存储过程和函数
    --triggers \                    # 包含触发器
    --events \                      # 包含事件调度器
    --set-gtid-purged=AUTO \        # GTID处理(自动判断)
    --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
mysqldump -u backup -p'BackupPass@2024' \
    --single-transaction \
    --source-data=2 \               # 新参数名
    --all-databases \
    | gzip > /backup/mysql/full/all_db_$(date +%Y%m%d).sql.gz

参数说明表

参数 说明 推荐
--single-transaction InnoDB一致性读,备份期间不锁表 必须
--source-data=2 记录binlog位置,2表示注释形式 推荐
--routines 包含存储过程和函数 推荐
--triggers 包含触发器 推荐
--events 包含事件 推荐
--set-gtid-purged GTID处理:OFF/ON/AUTO AUTO
--quick 大表逐行读取,节省内存 推荐
--lock-tables 锁定所有表(MyISAM) 非InnoDB时
--flush-logs 备份前刷新binlog 按需
--hex-blob BLOB字段十六进制导出 推荐
--compress 压缩传输(远程备份) 远程时
2.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

# 备份并流式传输到远程
xtrabackup --backup \
    --user=backup \
    --password='BackupPass@2024' \
    --stream=xbstream \
    --compress \
    | ssh backup@remote-server "cat > /backup/mysql/full_$(date +%Y%m%d).xbstream"

# 准备备份(应用redo log)
xtrabackup --prepare \
    --target-dir=/backup/mysql/full/20240115

# 恢复备份
xtrabackup --copy-back \
    --target-dir=/backup/mysql/full/20240115

# 设置权限
chown -R mysql:mysql /data/mysql/data
2.2.4 XtraBackup增量备份
复制代码
# 第一步:创建全量备份(基准备份)
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

# 准备增量备份(恢复前必须)
# 1. 准备基准备份(不应用redo log)
xtrabackup --prepare \
    --apply-log-only \
    --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

2.3 启动和验证

2.3.1 备份验证方法

方法一:恢复到测试环境验证

复制代码
#!/bin/bash
# 文件:/backup/mysql/scripts/verify_backup.sh
# 功能:备份验证脚本

BACKUP_FILE=$1
TEST_DATADIR="/tmp/mysql_verify_$(date +%s)"
TEST_PORT=3307

echo"Starting backup verification..."

# 解压备份(如果是压缩的)
if [[ $BACKUP_FILE == *.gz ]]; then
    gunzip -c $BACKUP_FILE > /tmp/verify_backup.sql
    BACKUP_FILE="/tmp/verify_backup.sql"
fi

# 创建临时数据目录
mkdir -p $TEST_DATADIR
mysqld --initialize-insecure --datadir=$TEST_DATADIR --user=mysql

# 启动临时MySQL实例
mysqld --datadir=$TEST_DATADIR --port=$TEST_PORT --socket=/tmp/mysql_verify.sock &
MYSQL_PID=$!
sleep 10

# 导入备份
mysql -uroot -S /tmp/mysql_verify.sock < $BACKUP_FILE
IMPORT_STATUS=$?

# 验证数据
if [ $IMPORT_STATUS -eq 0 ]; then
    echo"Checking tables..."
    mysql -uroot -S /tmp/mysql_verify.sock -e "
        SELECT table_schema, table_name, table_rows
        FROM information_schema.TABLES
        WHERE table_schema NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
        ORDER BY table_schema, table_name;
    "
    echo"Backup verification PASSED"
else
    echo"Backup verification FAILED"
fi

# 清理
kill$MYSQL_PID 2>/dev/null
rm -rf $TEST_DATADIR /tmp/mysql_verify.sock /tmp/verify_backup.sql

方法二:检查备份文件完整性

复制代码
# 检查mysqldump备份
# 文件应该以特定注释开头和结尾
head -20 /backup/mysql/full/all_db_20240115.sql
tail -20 /backup/mysql/full/all_db_20240115.sql

# 检查是否有错误
grep -i "error\|warning" /backup/mysql/full/all_db_20240115.sql

# 检查XtraBackup备份
# 查看备份信息
cat /backup/mysql/full/20240115/xtrabackup_checkpoints
# backup_type = full-backuped
# from_lsn = 0
# to_lsn = 123456789
# last_lsn = 123456789

cat /backup/mysql/full/20240115/xtrabackup_info
2.3.2 备份元数据记录
复制代码
-- 创建备份记录表
CREATEDATABASEIFNOTEXISTS backup_meta;
USE backup_meta;

CREATETABLE backup_history (
    idINT AUTO_INCREMENT PRIMARY KEY,
    backup_type ENUM('full', 'incremental', 'binlog') NOTNULL,
    backup_tool VARCHAR(50) NOTNULL,
    backup_path VARCHAR(500) NOTNULL,
    backup_size_bytes BIGINT,
    compressed_size_bytes BIGINT,
    start_time DATETIME NOTNULL,
    end_time DATETIME,
    duration_seconds INT,
    status ENUM('running', 'success', 'failed') DEFAULT'running',
    binlog_file VARCHAR(100),
    binlog_position BIGINT,
    gtid_executed TEXT,
    error_message TEXT,
    verified TINYINTDEFAULT0,
    verified_time DATETIME,
    INDEX idx_backup_time (start_time),
    INDEX idx_status (status)
) ENGINE=InnoDB;

-- 插入备份记录示例
INSERTINTO backup_history (backup_type, backup_tool, backup_path, start_time, binlog_file, binlog_position)
VALUES ('full', 'xtrabackup', '/backup/mysql/full/20240115', NOW(), 'mysql-bin.000123', 456789);

三、示例代码和配置

3.1 完整配置示例

3.1.1 生产环境mysqldump备份脚本
复制代码
#!/bin/bash
# 文件:/backup/mysql/scripts/mysqldump_backup.sh
# 功能:生产环境mysqldump备份脚本
# 用法:./mysqldump_backup.sh [full|single] [database_name]

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
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
}

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"
3.1.2 生产环境XtraBackup备份脚本
复制代码
#!/bin/bash
# 文件:/backup/mysql/scripts/xtrabackup_backup.sh
# 功能:生产环境XtraBackup备份脚本
# 用法:./xtrabackup_backup.sh [full|incremental]

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
INCR_RETENTION_DAYS=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 [full|incremental]"
        exit 1
        ;;
esac

cleanup_old_backups

log"Backup script completed"
3.1.3 定时任务配置
复制代码
# 编辑crontab
crontab -e

# mysqldump备份策略(小型数据库)
# 每天凌晨2点执行全备
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>&1

3.2 实际应用案例

3.2.1 案例一:误删数据恢复

场景:开发人员误执行DELETE语句,删除了users表中today的数据

复制代码
-- 误操作SQL
DELETE FROM users WHERE created_at >= '2024-01-15';
-- 应该是:DELETE FROM users WHERE created_at >= '2024-01-15' AND status = 'inactive';

恢复步骤

复制代码
# 第一步:立即记录当前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提取特定表
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

# 第四步:确认备份时间点
grep "CHANGE MASTER TO" /tmp/restore.sql | head -1
# -- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000145', MASTER_LOG_POS=789012;

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

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

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

场景:需要将数据库恢复到指定时间点(2024-01-15 14:30:00)

复制代码
#!/bin/bash
# 文件:point_in_time_recovery.sh
# 功能:时间点恢复脚本

RECOVERY_TIME="2024-01-15 14:30:00"
BACKUP_DIR="/backup/mysql/full/20240115"
BINLOG_DIR="/data/mysql/binlog"
RECOVERY_DIR="/data/mysql_recovery"

# 第一步:准备备份
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}"
3.2.3 案例三:大表备份和恢复策略

场景:orders表有5亿条数据,单表500GB

复制代码
# 备份策略:分区导出
#!/bin/bash
# 文件:large_table_backup.sh

TABLE="orders"
DB="ecommerce"
BACKUP_DIR="/backup/mysql/large_tables"
DATE=$(date +%Y%m%d)
PARALLEL_JOBS=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 <= ${END_ID}" \
        ${DB}${TABLE} | gzip > ${BACKUP_DIR}/${DATE}/${TABLE}_part${i}.sql.gz &
done

# 等待所有任务完成
wait

# 导出表结构
mysqldump -ubackup -p'BackupPass@2024' \
    --no-data \
    ${DB}${TABLE} > ${BACKUP_DIR}/${DATE}/${TABLE}_schema.sql

echo"Large table backup completed"

# 恢复策略:并行导入
#!/bin/bash
# 文件:large_table_restore.sh

DB="ecommerce"
TABLE="orders"
BACKUP_DIR="/backup/mysql/large_tables/20240115"
PARALLEL_JOBS=4

# 先导入表结构
mysql -uroot -p ${DB} < ${BACKUP_DIR}/${TABLE}_schema.sql

# 禁用索引(加速导入)
mysql -uroot -p -e "ALTER TABLE ${DB}.${TABLE} DISABLE KEYS"

# 并行导入数据
for f in${BACKUP_DIR}/${TABLE}_part*.sql.gz; do
    echo"Importing ${f}..."
    gunzip -c $f | mysql -uroot -p ${DB} &

    # 控制并行数
    while [ $(jobs -r | wc -l) -ge ${PARALLEL_JOBS} ]; do
        sleep 1
    done
done

wait

# 重建索引
mysql -uroot -p -e "ALTER TABLE ${DB}.${TABLE} ENABLE KEYS"

echo"Large table restore completed"

四、最佳实践和注意事项

4.1 最佳实践

4.1.1 备份策略设计

小型数据库(<50GB)

复制代码
备份策略:
- 工具:mysqldump
- 全备:每天凌晨
- binlog:实时归档
- 保留:7天

恢复目标:
- RTO: 1小时
- RPO: 1小时

中型数据库(50-500GB)

复制代码
备份策略:
- 工具:XtraBackup
- 全备:每周一次
- 增量:每天一次
- binlog:每小时归档
- 保留:全备30天,增量7天

恢复目标:
- RTO: 2小时
- RPO: 1小时

大型数据库(>500GB)

复制代码
备份策略:
- 工具:XtraBackup流式备份
- 全备:每周一次,流式传输到备份存储
- 增量:每天一次
- binlog:实时归档
- 异地备份:同步到灾备中心
- 保留:全备60天,增量14天

恢复目标:
- RTO: 4小时
- RPO: 5分钟
4.1.2 安全加固
复制代码
# 1. 备份文件加密
# 使用openssl加密
mysqldump ... | gzip | openssl enc -aes-256-cbc -salt -pass pass:YourEncryptKey > backup.sql.gz.enc

# 解密
openssl enc -d -aes-256-cbc -pass pass:YourEncryptKey -in backup.sql.gz.enc | gunzip > backup.sql

# 2. XtraBackup加密备份
xtrabackup --backup \
    --user=backup \
    --password='BackupPass@2024' \
    --target-dir=/backup/mysql/full/20240115 \
    --encrypt=AES256 \
    --encrypt-key-file=/etc/mysql/backup.key

# 3. 备份文件权限控制
chmod 600 /backup/mysql/full/*.sql.gz
chown mysql:mysql /backup/mysql/full/*.sql.gz

# 4. 备份目录访问控制
chmod 750 /backup/mysql
chown mysql:mysql /backup/mysql
4.1.3 高可用设计
复制代码
# 1. 在从库执行备份,不影响主库性能
# XtraBackup从库备份
xtrabackup --backup \
    --user=backup \
    --password='BackupPass@2024' \
    --target-dir=/backup/mysql/full/20240115 \
    --slave-info \       # 记录主库binlog位置
    --safe-slave-backup  # 安全从库备份

# 2. 备份到远程存储
# 流式备份到S3
xtrabackup --backup \
    --user=backup \
    --password='BackupPass@2024' \
    --stream=xbstream \
    --compress | aws s3 cp - s3://backup-bucket/mysql/full_$(date +%Y%m%d).xbstream

# 3. 多地备份
# 本地 + NFS + 云存储

4.2 注意事项

4.2.1 常见错误
错误场景 原因 解决方案
mysqldump导出乱码 字符集不匹配 添加--default-character-set=utf8mb4
XtraBackup备份失败 版本不匹配 XtraBackup版本需与MySQL匹配
恢复后数据不完整 binlog未应用 确保应用所有增量binlog
备份文件损坏 传输错误或存储问题 启用校验和验证
恢复后权限丢失 未备份mysql库 使用--all-databases
4.2.2 配置注意事项
复制代码
-- 1. 确保binlog开启
SHOWVARIABLESLIKE'log_bin';
-- 必须为ON

-- 2. binlog格式
SHOWVARIABLESLIKE'binlog_format';
-- 推荐ROW格式

-- 3. GTID配置
SHOWVARIABLESLIKE'gtid_mode';
-- 推荐开启GTID

-- 4. binlog保留时间
SHOWVARIABLESLIKE'binlog_expire_logs_seconds';
-- 设置合理的保留时间,确保能覆盖两个全备周期
SETGLOBAL binlog_expire_logs_seconds = 604800;  -- 7天
4.2.3 恢复测试
复制代码
#!/bin/bash
# 定期恢复测试脚本
# 建议:每周执行一次恢复演练

# 1. 选择最近的备份
LATEST_BACKUP=$(ls -t /backup/mysql/full/*.sql.gz | head -1)

# 2. 恢复到测试环境
# ...

# 3. 数据一致性验证
mysql -uroot -p -e "
SELECT
    table_schema,
    table_name,
    table_rows,
    data_length,
    index_length
FROM information_schema.TABLES
WHERE table_schema NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
ORDER BY data_length DESC
LIMIT 20;
"

# 4. 记录恢复时间和结果
echo"Recovery test completed at $(date)" >> /backup/mysql/logs/recovery_test.log

五、故障排查和监控

5.1 故障排查

5.1.1 mysqldump常见问题
复制代码
# 问题1:备份过程中断
# 原因:连接超时
# 解决:
mysqldump --net-buffer-length=16K \
    --max-allowed-packet=1G \
    --single-transaction \
    ...

# 问题2:内存不足
# 原因:大表导出占用过多内存
# 解决:使用--quick选项
mysqldump --quick ...

# 问题3:锁等待超时
# 原因:备份期间有大事务
# 解决:使用--single-transaction,在低峰期执行

# 问题4:导入失败
# 查看错误日志
mysql -uroot -p 2>&1 | tee import.log
# 检查外键约束
SET FOREIGN_KEY_CHECKS=0;
# 导入...
SET FOREIGN_KEY_CHECKS=1;
5.1.2 XtraBackup常见问题
复制代码
# 问题1:版本不兼容
xtrabackup: Error: unsupported server version
# 解决:升级XtraBackup到匹配版本

# 问题2:磁盘空间不足
xtrabackup: Error: Failed to write to file
# 解决:清理空间或使用流式备份

# 问题3:prepare失败
xtrabackup: error: applying incremental backup
# 原因:增量备份链断裂
# 解决:重新从全备恢复

# 问题4:复制信息丢失
# 解决:使用--slave-info选项
cat backup_dir/xtrabackup_slave_info

5.2 性能监控

5.2.1 备份监控指标
指标 说明 告警阈值
备份持续时间 备份执行耗时 超过预期1.5倍
备份大小 备份文件大小 异常增长>20%
备份成功率 备份成功次数/总次数 <100%
备份延迟 上次成功备份距今时间 >设定周期
binlog归档延迟 binlog归档滞后 >1小时
5.2.2 监控脚本
复制代码
#!/bin/bash
# 文件:/backup/mysql/scripts/backup_monitor.sh
# 功能:备份监控和告警

BACKUP_DIR="/backup/mysql/full"
ALERT_URL="https://alert.example.com/api/alert"
MAX_BACKUP_AGE=86400  # 24小时

# 检查最近备份时间
latest_backup=$(ls -t ${BACKUP_DIR}/*.sql.gz 2>/dev/null | head -1)

if [ -z "$latest_backup" ]; then
    echo"No backup found!"
    curl -X POST ${ALERT_URL} -d '{"level":"critical","message":"No MySQL backup found"}'
    exit 1
fi

backup_time=$(stat -c %Y "$latest_backup")
current_time=$(date +%s)
backup_age=$((current_time - backup_time))

if [ $backup_age -gt $MAX_BACKUP_AGE ]; then
    echo"Backup is too old: ${backup_age} seconds"
    curl -X POST ${ALERT_URL} -d "{\"level\":\"warning\",\"message\":\"MySQL backup is ${backup_age}s old\"}"
fi

# 检查备份大小趋势
current_size=$(stat -c %s "$latest_backup")
# 与上次备份比较...

echo"Backup monitoring completed"

5.3 备份与恢复

5.3.1 灾难恢复演练
复制代码
#!/bin/bash
# 文件:disaster_recovery_drill.sh
# 功能:灾难恢复演练脚本

echo"=== MySQL Disaster Recovery Drill ==="
echo"Start time: $(date)"

# 模拟场景:主库完全宕机,需要从备份恢复

# 1. 记录当前状态
mysql -uroot -p -e "SELECT COUNT(*) FROM mydb.orders" > /tmp/pre_drill_count.txt

# 2. 停止MySQL
systemctl stop mysqld

# 3. 备份当前数据目录(安全起见)
mv /data/mysql/data /data/mysql/data_drill_backup

# 4. 恢复最新备份
LATEST_BACKUP=$(ls -td /backup/mysql/full/*/ | head -1)
xtrabackup --prepare --target-dir=${LATEST_BACKUP}
xtrabackup --copy-back --target-dir=${LATEST_BACKUP}
chown -R mysql:mysql /data/mysql/data

# 5. 启动MySQL
systemctl start mysqld

# 6. 应用binlog(如需要)
# ...

# 7. 验证数据
mysql -uroot -p -e "SELECT COUNT(*) FROM mydb.orders" > /tmp/post_drill_count.txt

# 8. 比较结果
echo"Pre-drill count:"
cat /tmp/pre_drill_count.txt
echo"Post-drill count:"
cat /tmp/post_drill_count.txt

# 9. 恢复原数据(演练环境)
# systemctl stop mysqld
# rm -rf /data/mysql/data
# mv /data/mysql/data_drill_backup /data/mysql/data
# systemctl start mysqld

echo"=== Drill completed ==="
echo"End time: $(date)"

附录

A. 命令速查表

命令 说明
mysqldump -u user -p --all-databases 备份所有数据库
mysqldump --single-transaction 一致性读备份
mysqldump --source-data=2 记录binlog位置
xtrabackup --backup 全量备份
xtrabackup --backup --incremental-basedir= 增量备份
xtrabackup --prepare 准备备份
xtrabackup --copy-back 恢复备份
mysqlbinlog --start-position= --stop-position= binlog解析

B. 配置参数详解

参数 默认值 说明
--single-transaction OFF InnoDB一致性读
--source-data 0 记录binlog位置(0/1/2)
--routines OFF 包含存储过程
--triggers ON 包含触发器
--events OFF 包含事件
--compress OFF 压缩备份
--parallel 1 并行线程数
--stream - 流式输出格式

C. 术语表

术语 英文 说明
全量备份 Full Backup 完整数据备份
增量备份 Incremental Backup 只备份变化数据
差异备份 Differential Backup 相对全备的变化
物理备份 Physical Backup 复制数据文件
逻辑备份 Logical Backup 导出SQL语句
热备份 Hot Backup 运行时备份
RTO Recovery Time Objective 恢复时间目标
RPO Recovery Point Objective 恢复点目标
PITR Point-In-Time Recovery 时间点恢复
相关推荐
L1624762 小时前
基于 Xenon 实现 MySQL 高可用集群(完整配置教程,含监控告警 + 定时备份)
android·mysql·adb
补三补四2 小时前
Django与模板
数据库·python·django·sqlite
what丶k2 小时前
SQL三大核心查询语法(WHERE/ORDER BY/GROUP BY)综合运用指南
大数据·数据库·sql·mysql·面试
程序辅导开发2 小时前
django体育用品数据分析系统 毕业设计---附源码28946
数据库·vue.js·python·mysql·django·sqlite
工业互联网专业2 小时前
基于Django的智能水果销售系统设计
数据库·vue.js·django·毕业设计·源码·课程设计
N***77882 小时前
【玩转全栈】----Django模板语法、请求与响应
数据库·python·django
忧郁的Mr.Li2 小时前
Linux下MySQL8的密码忘记之后重置密码
linux·运维·mysql
霑潇雨2 小时前
题解 | 分析每个商品在不同时间段的销售情况
数据库·sql·算法·笔试
Watermelo6172 小时前
随机扣款实现赛博共产主义,《明日方舟:终末地》公测支付事故复盘
数据库·后端·游戏程序·技术美术·用户体验·游戏策划·游戏美术