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 | 恢复点目标,最多丢失多少数据 |
全部0条评论
快来发表一下你的评论吧 !