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

描述

MySQL备份恢复策略:mysqldump、XtraBackup与binlog实战

一、概述

1.1 背景介绍

备份是数据库运维中最重要也最容易被忽视的环节。"重要"体现在数据丢失时备份是唯一的救命稻草,"忽视"体现在很多团队有备份脚本但从未做过恢复演练,等到真正需要恢复时才发现备份文件损坏或恢复流程不熟悉。

MySQL 的备份策略需要在 RTO(恢复时间目标)、RPO(恢复点目标)和备份成本之间做权衡。没有一种方案能同时满足"备份快、恢复快、存储小",选型时必须明确业务对这三个维度的优先级。

1.2 技术特点

mysqldump:逻辑备份,SQL 文本格式,可读性强,跨版本兼容,适合小库

XtraBackup:物理备份,直接复制 InnoDB 数据文件,速度快,支持增量,适合大库

binlog:增量日志,配合全量备份实现 PITR(Point-in-Time Recovery),将 RPO 降到分钟级

1.3 适用场景

mysqldump:数据库总量 < 50GB,需要跨版本迁移,或需要备份特定表

XtraBackup:数据库总量 > 50GB,需要快速恢复,或需要搭建从库

binlog PITR:对 RPO 要求严格(< 5 分钟),需要恢复到任意时间点

1.4 环境要求

组件 版本要求 说明
MySQL 8.4.x LTS XtraBackup 需要版本匹配
XtraBackup 8.4.x 必须与 MySQL 大版本一致
操作系统 Ubuntu 22.04+ / CentOS 8+ 推荐 Ubuntu 22.04
存储空间 数据目录 2-3 倍 全量备份 + 增量备份空间
网络 备份服务器与 DB 同机房 减少备份传输时间

二、详细步骤

2.1 三种备份方式对比

维度 mysqldump XtraBackup binlog
备份类型 逻辑备份 物理备份 增量日志
备份速度 慢(需读取所有数据) 快(文件级复制) 实时
恢复速度 慢(需重新执行 SQL) 快(直接替换文件) 需配合全量
备份大小 压缩后较小 与数据目录相当 取决于写入量
一致性 需要 --single-transaction 热备,不锁表 需要全量基准
跨版本 支持 不支持 不支持
适用库大小 < 50GB 任意大小 配合全量使用

2.2 mysqldump 配置与使用

2.2.1 核心参数详解

 

# 生产环境标准备份命令
mysqldump 
  --host=127.0.0.1 
  --port=3306 
  --user=backup_user 
  --password="${BACKUP_PASS}" 
  --single-transaction         # InnoDB 一致性快照,不锁表(关键参数)
  --master-data=2              # 记录 binlog position,注释形式写入备份文件
  --source-data=2              # 8.4 中替代 --master-data
  --flush-logs                 # 备份前切换 binlog,便于后续 PITR
  --hex-blob                   # BLOB 字段用十六进制,避免字符集问题
  --routines                   # 备份存储过程和函数
  --triggers                   # 备份触发器
  --events                     # 备份定时事件
  --compress                   # 客户端与服务端传输压缩
  --databases production_db 
  | gzip -9 > /backup/mysql/production_db_$(date +%Y%m%d_%H%M%S).sql.gz

 

2.2.2 备份账号权限

 

-- 创建最小权限备份账号
CREATE USER 'backup_user'@'127.0.0.1'
  IDENTIFIED WITH caching_sha2_password BY 'BackupPass@2024';

GRANT SELECT, SHOW VIEW, TRIGGER, LOCK TABLES,
      REPLICATION CLIENT, PROCESS, EVENT
  ON *.* TO 'backup_user'@'127.0.0.1';

-- 8.4 中 REPLICATION CLIENT 已改名为 REPLICATION_CLIENT_ADMIN
-- 但旧名称仍兼容
FLUSH PRIVILEGES;

 

2.2.3 恢复流程

 

# 解压并恢复
gunzip < /backup/mysql/production_db_20240115_020000.sql.gz 
  | mysql -u root -p production_db

# 查看备份文件中的 binlog position(用于 PITR)
zcat /backup/mysql/production_db_20240115_020000.sql.gz 
  | grep "CHANGE MASTER|CHANGE REPLICATION SOURCE" | head -5
# 输出示例:
# -- CHANGE REPLICATION SOURCE TO SOURCE_LOG_FILE='mysql-bin.000123', SOURCE_LOG_POS=4567890;

 

2.3 XtraBackup 全量 + 增量备份

2.3.1 安装 XtraBackup 8.4

 

# Ubuntu 22.04
wget https://downloads.percona.com/downloads/percona-xtrabackup-8.4/
percona-xtrabackup-8.4.0-1/binary/debian/jammy/x86_64/
percona-xtrabackup-84_8.4.0-1.jammy_amd64.deb

dpkg -i percona-xtrabackup-84_8.4.0-1.jammy_amd64.deb
apt-get install -f  # 安装依赖

 

2.3.2 全量备份

 

# 全量备份
xtrabackup 
  --backup 
  --user=backup_user 
  --password="${BACKUP_PASS}" 
  --host=127.0.0.1 
  --target-dir=/backup/xtrabackup/full_$(date +%Y%m%d) 
  --compress                   # 压缩备份文件
  --compress-threads=4         # 压缩并行线程数
  --parallel=4                 # 备份并行线程数
  --slave-info                 # 如果是从库,记录主库 binlog position
  --safe-slave-backup           # 从库备份时暂停复制,确保一致性

# 备份完成后准备(prepare)阶段
# prepare 阶段将 redo log 应用到数据文件,使备份达到一致状态
xtrabackup 
  --prepare 
  --target-dir=/backup/xtrabackup/full_20240115

 

2.3.3 增量备份

 

# 基于全量备份做第一次增量
xtrabackup 
  --backup 
  --user=backup_user 
  --password="${BACKUP_PASS}" 
  --target-dir=/backup/xtrabackup/inc_$(date +%Y%m%d_%H) 
  --incremental-basedir=/backup/xtrabackup/full_20240115 
  --compress 
  --compress-threads=4 
  --parallel=4

# 基于上一次增量做第二次增量
xtrabackup 
  --backup 
  --user=backup_user 
  --password="${BACKUP_PASS}" 
  --target-dir=/backup/xtrabackup/inc_20240115_12 
  --incremental-basedir=/backup/xtrabackup/inc_20240115_06 
  --compress 
  --compress-threads=4

 

2.3.4 增量备份恢复流程

 

# 步骤 1:准备全量备份(不应用 redo log,等待增量合并)
xtrabackup --prepare --apply-log-only 
  --target-dir=/backup/xtrabackup/full_20240115

# 步骤 2:合并第一个增量到全量
xtrabackup --prepare --apply-log-only 
  --target-dir=/backup/xtrabackup/full_20240115 
  --incremental-dir=/backup/xtrabackup/inc_20240115_06

# 步骤 3:合并最后一个增量(去掉 --apply-log-only,应用 redo log)
xtrabackup --prepare 
  --target-dir=/backup/xtrabackup/full_20240115 
  --incremental-dir=/backup/xtrabackup/inc_20240115_12

# 步骤 4:停止 MySQL,恢复数据文件
systemctl stop mysql
mv /var/lib/mysql /var/lib/mysql.bak
xtrabackup --copy-back 
  --target-dir=/backup/xtrabackup/full_20240115
chown -R mysql:mysql /var/lib/mysql
systemctl start mysql

 

2.4 binlog PITR(基于时间点恢复)

2.4.1 前提条件

 

# MySQL 必须开启 binlog
[mysqld]
log_bin = /var/log/mysql/mysql-bin
binlog_format = ROW
expire_logs_days = 0            # 不自动删除,由备份脚本管理
binlog_expire_logs_seconds = 604800  # 7 天,8.4 推荐用这个参数

 

2.4.2 PITR 恢复流程

 

# 场景:全量备份时间 2024-01-15 0200,误操作发生在 1000
# 目标:恢复到 1059

# 步骤 1:恢复全量备份(mysqldump 方式)
gunzip < /backup/mysql/production_db_20240115_020000.sql.gz 
  | mysql -u root -p production_db

# 步骤 2:从备份文件获取 binlog position
BINLOG_FILE="mysql-bin.000123"
BINLOG_POS=4567890

# 步骤 3:应用 binlog 到误操作前一刻
mysqlbinlog 
  --start-position=${BINLOG_POS} 
  --stop-datetime="2024-01-15 1059" 
  --database=production_db 
  /var/log/mysql/mysql-bin.000123 
  /var/log/mysql/mysql-bin.000124 
  | mysql -u root -p production_db

# 如果需要跳过某个误操作事务(已知 GTID)
mysqlbinlog 
  --start-position=${BINLOG_POS} 
  --stop-datetime="2024-01-15 1059" 
  --exclude-gtids="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee:12345" 
  /var/log/mysql/mysql-bin.000123 
  | mysql -u root -p production_db

 

三、示例代码和配置

3.1 自动化备份脚本

 

#!/bin/bash
# 文件名:mysql-backup.sh
# 功能:MySQL 全量备份 + binlog 归档,支持本地和远程存储

set -euo pipefail

# 配置
MYSQL_USER="backup_user"
MYSQL_PASS="${MYSQL_BACKUP_PASS}"  # 从环境变量读取,不硬编码
MYSQL_HOST="127.0.0.1"
BACKUP_BASE="/backup/mysql"
BINLOG_DIR="/var/log/mysql"
REMOTE_BUCKET="s3://company-mysql-backup"
RETENTION_DAYS=7
DATE=$(date +%Y%m%d_%H%M%S)
LOG_FILE="/var/log/mysql-backup.log"

log() {
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "${LOG_FILE}"
}

# 全量备份
full_backup() {
    local backup_file="${BACKUP_BASE}/full_${DATE}.sql.gz"
    log "开始全量备份: ${backup_file}"

    mysqldump 
        --host="${MYSQL_HOST}" 
        --user="${MYSQL_USER}" 
        --password="${MYSQL_PASS}" 
        --single-transaction 
        --source-data=2 
        --flush-logs 
        --hex-blob 
        --routines 
        --triggers 
        --events 
        --all-databases 
        2>>"${LOG_FILE}" 
        | gzip -6 > "${backup_file}"

    local size
    size=$(du -sh "${backup_file}" | cut -f1)
    log "全量备份完成,大小: ${size}"

    # 上传到对象存储
    aws s3 cp "${backup_file}" "${REMOTE_BUCKET}/full/" 
        --storage-class STANDARD_IA 
        2>>"${LOG_FILE}"
    log "备份已上传到 S3"
}

# binlog 归档
archive_binlog() {
    log "开始归档 binlog"
    # 切换到新的 binlog 文件
    mysql --host="${MYSQL_HOST}" 
          --user="${MYSQL_USER}" 
          --password="${MYSQL_PASS}" 
          -e "FLUSH BINARY LOGS;" 2>>"${LOG_FILE}"

    # 获取当前 binlog 文件列表(排除最新的,它还在写入)
    local current_binlog
    current_binlog=$(mysql --host="${MYSQL_HOST}" 
                           --user="${MYSQL_USER}" 
                           --password="${MYSQL_PASS}" 
                           -sNe "SHOW MASTER STATUS" 2>>"${LOG_FILE}" | awk '{print $1}')

    # 上传除当前文件外的所有 binlog
    for binlog in "${BINLOG_DIR}"/mysql-bin.[0-9]*; do
        local filename
        filename=$(basename "${binlog}")
        if [[ "${filename}" != "${current_binlog}" ]]; then
            aws s3 cp "${binlog}" "${REMOTE_BUCKET}/binlog/" 
                --storage-class STANDARD_IA 
                2>>"${LOG_FILE}" && 
            log "已归档: ${filename}"
        fi
    done
}

# 清理过期备份
cleanup_old_backups() {
    log "清理 ${RETENTION_DAYS} 天前的本地备份"
    find "${BACKUP_BASE}" -name "full_*.sql.gz" 
        -mtime "+${RETENTION_DAYS}" -delete
}

# 备份验证(随机抽查)
verify_backup() {
    local latest_backup
    latest_backup=$(ls -t "${BACKUP_BASE}"/full_*.sql.gz | head -1)
    log "验证备份文件: ${latest_backup}"

    # 检查文件完整性
    if gzip -t "${latest_backup}" 2>>"${LOG_FILE}"; then
        log "备份文件完整性验证通过"
    else
        log "ERROR: 备份文件损坏!"
        exit 1
    fi

    # 检查备份文件包含关键表
    if zcat "${latest_backup}" | grep -q "CREATE TABLE.*orders"; then
        log "关键表验证通过"
    else
        log "ERROR: 备份文件缺少关键表!"
        exit 1
    fi
}

main() {
    mkdir -p "${BACKUP_BASE}"
    log "=== MySQL 备份开始 ==="
    full_backup
    archive_binlog
    verify_backup
    cleanup_old_backups
    log "=== MySQL 备份完成 ==="
}

main "$@"

 

3.2 恢复演练脚本

 

#!/bin/bash
# 文件名:mysql-restore-test.sh
# 功能:在测试环境验证备份可恢复性

BACKUP_FILE=$1
TEST_DB="restore_test_$(date +%Y%m%d)"
MYSQL_ROOT_PASS="${MYSQL_ROOT_PASS}"

if [[ -z "${BACKUP_FILE}" ]]; then
    echo "用法: $0 "
    exit 1
fi

echo "创建测试数据库: ${TEST_DB}"
mysql -u root -p"${MYSQL_ROOT_PASS}" -e "CREATE DATABASE ${TEST_DB};"

echo "开始恢复备份..."
time gunzip < "${BACKUP_FILE}" 
    | mysql -u root -p"${MYSQL_ROOT_PASS}" "${TEST_DB}"

echo "验证数据完整性..."
mysql -u root -p"${MYSQL_ROOT_PASS}" "${TEST_DB}" <<'EOF'
-- 检查关键表行数
SELECT table_name, table_rows
FROM information_schema.TABLES
WHERE table_schema = DATABASE()
ORDER BY table_rows DESC
LIMIT 10;
EOF

echo "清理测试数据库"
mysql -u root -p"${MYSQL_ROOT_PASS}" -e "DROP DATABASE ${TEST_DB};"
echo "恢复演练完成"

 

四、最佳实践和注意事项

4.1 最佳实践

4.1.1 备份策略设计

根据 RTO/RPO 目标选择备份组合:

业务级别 RPO 目标 RTO 目标 推荐策略
核心交易 < 5 分钟 < 30 分钟 XtraBackup 每日全量 + binlog 实时归档
一般业务 < 1 小时 < 2 小时 mysqldump 每日全量 + binlog 每小时归档
非关键数据 < 24 小时 < 4 小时 mysqldump 每日全量

4.1.2 3-2-1 备份原则

3 份备份:原始数据 + 本地备份 + 远程备份

2 种介质:本地磁盘 + 对象存储(S3/OSS)

1 份异地:备份存储在不同地域,防止机房级故障

4.1.3 备份监控

 

# 检查备份是否按时完成(Prometheus 告警规则)
# 如果最新备份文件超过 25 小时未更新,触发告警
find /backup/mysql -name "full_*.sql.gz" -mmin -1500 | wc -l
# 结果为 0 时说明备份失败,需要告警

 

4.2 注意事项

4.2.1 常见错误

 警告:以下错误在生产环境中曾导致数据无法恢复。

mysqldump 不加 --single-transaction 会锁表,对 InnoDB 表必须加此参数

XtraBackup 版本必须与 MySQL 版本严格匹配,8.0 的 XtraBackup 无法备份 8.4 的 MySQL

binlog 必须在全量备份完成后才能删除,否则 PITR 会有空洞

4.2.2 常见错误排查

错误现象 原因分析 解决方案
mysqldump 卡住不动 有长事务持有表锁 SHOW PROCESSLIST  找到长事务并 KILL
XtraBackup 报 "redo log archiving" 错误 MySQL 8.4 新特性冲突 升级 XtraBackup 到对应版本
binlog 恢复后数据不一致 binlog position 记录错误 使用 GTID 模式替代 position 模式
备份文件 gzip 损坏 磁盘空间不足导致写入中断 备份前检查磁盘空间,设置告警阈值 80%
恢复速度极慢 innodb_buffer_pool_size 太小 恢复时临时调大 buffer pool

五、故障排查和监控

5.1 备份失败排查

5.1.1 常见问题排查

问题一:mysqldump 连接超时

 

# 诊断:检查 MySQL 连接状态
mysql -u root -p -e "SHOW STATUS LIKE 'Threads_connected';"
mysql -u root -p -e "SHOW PROCESSLIST;" | grep -v Sleep

# 解决:增加超时参数
mysqldump --net-read-timeout=3600 --net-write-timeout=3600 ...

 

问题二:XtraBackup 备份中断

 

# 查看 XtraBackup 日志
tail -100 /backup/xtrabackup/full_20240115/xtrabackup_info

# 常见原因:磁盘空间不足
df -h /backup/

# 检查 MySQL 错误日志
tail -50 /var/log/mysql/error.log

 

问题三:binlog 文件缺失

 

-- 查看当前 binlog 列表
SHOW BINARY LOGS;

-- 查看 binlog 中的事件(验证内容)
SHOW BINLOG EVENTS IN 'mysql-bin.000123' LIMIT 20;

-- 如果 binlog 已被清理,检查 expire 配置
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';

 

5.2 性能监控

5.2.1 备份性能指标

 

# 监控备份速度(MB/s)
iostat -x 1 | grep -E "Device|sda"

# 监控备份进度(XtraBackup)
# XtraBackup 会输出已处理的数据量
tail -f /var/log/xtrabackup.log | grep "MB/s"

# 监控 mysqldump 进度
# 通过 performance_schema 查看当前执行的 SQL
mysql -e "SELECT * FROM performance_schema.processlist WHERE user='backup_user'G"

 

5.2.2 监控指标说明

指标名称 正常范围 告警阈值 说明
备份完成时间 < 4 小时 > 6 小时 超时说明数据量增长或性能下降
备份文件大小变化 ±20% > 50% 增长 突增可能是数据异常
binlog 生成速率 业务基线 基线 3x 突增可能是大批量写入
磁盘使用率 < 70% > 80% 备份磁盘空间告警

5.3 备份与恢复

5.3.1 定期恢复演练

 

# 每月执行一次恢复演练,验证备份可用性
# 在测试环境执行,记录恢复时间

# 1. 从 S3 下载最新备份
aws s3 cp s3://company-mysql-backup/full/full_latest.sql.gz /tmp/

# 2. 记录开始时间
START_TIME=$(date +%s)

# 3. 执行恢复
gunzip < /tmp/full_latest.sql.gz | mysql -u root -p test_restore_db

# 4. 计算恢复时间
END_TIME=$(date +%s)
echo "恢复耗时: $((END_TIME - START_TIME)) 秒"

# 5. 验证数据
mysql -u root -p test_restore_db -e "
  SELECT COUNT(*) as order_count FROM orders;
  SELECT MAX(created_at) as latest_order FROM orders;
"

 

六、总结

6.1 技术要点回顾

 方案选型:50GB 以下用 mysqldump,以上用 XtraBackup,两者都需要配合 binlog 实现 PITR

 备份验证:备份完成后必须验证文件完整性,每月做一次完整恢复演练

 3-2-1 原则:本地 + 远程双份存储,防止单点故障

 RTO/RPO 对齐:备份策略必须与业务的恢复目标对齐,不能凭感觉设计

6.2 进阶学习方向

MySQL InnoDB Cluster 内置备份:MySQL Shell 的 util.dumpInstance() 是 mysqldump 的现代替代品,支持并行导出,速度提升 10 倍以上

Percona XtraBackup 流式备份:直接通过网络流传输到备份服务器,无需本地中转

备份加密:使用 --encrypt 参数对备份文件加密,满足合规要求

6.3 参考资料

XtraBackup 8.4 官方文档

MySQL 8.4 备份恢复文档

MySQL Shell Dump & Load

附录

A. 命令速查表

 

# mysqldump 全库备份
mysqldump --single-transaction --source-data=2 --all-databases | gzip > backup.sql.gz

# XtraBackup 全量备份
xtrabackup --backup --target-dir=/backup/full --compress --parallel=4

# XtraBackup prepare
xtrabackup --prepare --target-dir=/backup/full

# 查看 binlog 内容
mysqlbinlog --base64-output=DECODE-ROWS -v mysql-bin.000123 | head -100

# PITR 恢复到指定时间
mysqlbinlog --start-position=4567890 --stop-datetime="2024-01-15 1059" mysql-bin.000123 | mysql -u root -p

# 查看备份文件中的 binlog position
zcat backup.sql.gz | grep "CHANGE REPLICATION SOURCE"

 

B. 配置参数详解

参数 推荐值 说明
binlog_expire_logs_seconds 604800 binlog 保留 7 天
sync_binlog 1 每次提交刷盘,防止 binlog 丢失
innodb_flush_log_at_trx_commit 1 事务提交时刷 redo log
max_binlog_size 1G 单个 binlog 文件最大 1GB

C. 术语表

术语 英文 解释
逻辑备份 Logical Backup 以 SQL 语句形式备份,可读性强
物理备份 Physical Backup 直接复制数据文件,速度快
增量备份 Incremental Backup 只备份上次备份后变化的数据
PITR Point-in-Time Recovery 基于时间点恢复,精确到秒
RTO Recovery Time Objective 恢复时间目标,故障到恢复的最长时间
RPO Recovery Point Objective 恢复点目标,最多丢失多少数据

 

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

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分