9.2 KiB
9.2 KiB
tags, create time
| tags | create time | |||||
|---|---|---|---|---|---|---|
|
2026-05-16 00:00 |
备份与恢复
概述
备份是数据安全最后一道防线。本章覆盖逻辑备份(mysqldump)、物理备份(Percona XtraBackup)、以及基于 binlog 的时间点恢复(PITR)三大核心方案。
备份策略全景
flowchart LR
subgraph "逻辑备份"
LD["mysqldump<br/>导出 SQL"]
LF["全量 + 增量<br/>依赖 binlog"]
end
subgraph "物理备份"
PD["Percona XtraBackup<br/>热拷贝ibd文件"]
PF["压缩 + 加密<br/>支持部分备份"]
end
subgraph "选择指南"
LS["数据量 < 50GB<br/>停机窗口可接受"]
PS["数据量 > 50GB<br/>需要 0 停机"]
end
LD --> LS
PD --> PS
style LS fill:#00B6BC,color:#fff
style PS fill:#00D866,color:#fff
mysqldump 逻辑备份
全量备份
# 最基础的全量备份
mysqldump -u root -p --all-databases > all_dbs_$(date +%F).sql
# 推荐的参数组合(保持一致性和完整性)
mysqldump \
-u root -p \
--single-transaction # MVCC 快照,不锁表
--flush-logs # 切换 binlog,便于后续恢复
--master-data=2 # 记录 binlog position(注释形式)
--routines # 包含存储过程和函数
--triggers # 包含触发器
--events # 包含事件调度器
--databases app_db report_db > backup_$(date +%F).sql
还原
# 全量还原
mysql -u root -p < backup_2026-05-16.sql
# 只还原某个表(先导出再导入)
mysqldump -u root -p app_db --table users > users.sql
mysql -u root -p app_db < users.sql
mysqldump 的优缺点
| 优点 | 缺点 |
|---|---|
| 简单可靠,几乎不会出错 | 大库慢(导入/导出) |
| 文本格式,可读可编辑 | 导出的数据和原始可能有差异(如字符集转换) |
| 自带权限和表结构定义 | 不支持增量备份 |
| 跨版本迁移方便 | 备份期间虽然有 --single-transaction,但 --flush-logs 仍需短暂锁表 |
Percona XtraBackup(物理备份)
全量备份
# 全量备份(热备,不停机)
xtrabackup --backup \
--target-dir=/data/backups/full/ \
--user=root --password=xxx \
--parallel=4 # 多线程并行拷贝
# 备份后的准备阶段(使备份可用于恢复)
xtrabackup --prepare --target-dir=/data/backups/full/
# 增量备份(基于上一次备份)
xtrabackup --backup \
--target-dir=/data/backups/inc1/ \
--incremental-basedir=/data/backups/full/ \
--user=root --password=xxx
# 合并增量到全量(减少恢复时间)
# 先对全量做 prepare,只应用已提交的事务,不回滚未提交的
xtrabackup --prepare --target-dir=/data/backups/full/ --apply-only
# 再将增量备份合并进来
xtrabackup --prepare --target-dir=/data/backups/full/ --incremental-dir=/data/backups/inc1/
# 如果有多个增量层,依次合并
xtrabackup --prepare --target-dir=/data/backups/full/ --incremental-dir=/data/backups/inc2/
恢复
# 1. 停止 MySQL
systemctl stop mysqld
# 2. 清空数据目录(或用新目录)
rm -rf /var/lib/mysql/*
# 3. 拷贝备份数据
cp -a /data/backups/full/. /var/lib/mysql/
# 4. 设置权限
chown -R mysql:mysql /var/lib/mysql
# 5. 启动 MySQL
systemctl start mysqld
XtraBackup 的优缺点
| 优点 | 缺点 |
|---|---|
| 热备份:备份期间业务不受影响 | 学习曲线较陡 |
| 速度快:直接拷贝 ibd 文件 | 需要安装额外软件 |
| 支持增量备份 | 不能跨 major version(8.0→8.0) |
| 恢复速度远快于 mysqldump | 占用较多磁盘空间(物理拷贝) |
PITR — 时间点恢复
结合全量备份 + binlog,可以将数据库恢复到任意精确时刻。
[!QUESTION] 思考:如果凌晨 3 点的备份完成时 binlog position 是
mysql-bin.000005:1234,而 10 点误操作发生在mysql-bin.000006中,该怎么办? 答案是:binlog 链不能断——从mysql-bin.000005:1234开始到当前为止的所有 binlog 文件都必须保留。任何缺失都会导致 PITR 失败。
timeline
title PITR 恢复流程
00:00 : 凌晨全量备份 (XtraBackup)
06:00 : 应用正常运行
10:00 : ⚠️ 误删表!DELETE FROM users;
10:01 : 检测到异常,开始恢复
恢复过程 : 1. 恢复全量备份到 00:00
: 2. 重放 binlog 到 09:59:59
: 3. 跳过误操作语句
: 4. 恢复到 10:00 之前的状态
# 第一步:恢复全量备份
# (前面已讲过 XtraBackup 恢复步骤)
# 第二步:找到误操作的准确位置
# 方法 A:用时间定位(推荐,最直观)
mysqlbinlog --stop-datetime='2026-05-16 09:59:59' /var/lib/mysql/mysql-bin.000006 > safe_events.sql
# 方法 B:搜索误操作语句,获取 position
mysqlbinlog /var/lib/mysql/mysql-bin.000006 | grep -B5 "DROP DATABASE\|DELETE FROM users"
# 找到对应的 DELETE 所在事务的 stop_position,在此之前停止
# 第三步:重放 binlog 到误操作之前(基于 position)
mysqlbinlog \
--start-position=1234 \
--stop-position=4567 \
/var/lib/mysql/mysql-bin.000005 > before_incident.sql
# 第四步:跳过误操作行,手动编辑 SQL 后重放其余部分
# 方法一:导出到文件后用 sed/grep 过滤掉误操作的 DDL/DML
mysqlbinlog /var/lib/mysql/mysql-bin.000005 > all_events.sql
# 用编辑器打开 all_events.sql,定位到误操作的 BEGIN/COMMIT 区间并删除
# 方法二:精确用 position 范围分段重放
mysqlbinlog \
--stop-position=4567 \
/var/lib/mysql/mysql-bin.000005 | mysql -u root -p # 重放到误操作前
mysqlbinlog \
--start-position=5000 \
/var/lib/mysql/mysql-bin.000005 | mysql -u root -p # 从安全点继续重放
# MySQL 8.0+ GTID 模式(推荐,更简洁)
# 包含 uuid 下 1~100 的 GTID,排除第 101 个事务(即误操作的那个)
mysqlbinlog \
--include-gtids='uuid:1-100' \
--exclude-gtids='uuid:101' \
mysql-bin.000005 | mysql -u root -p
[!WARNING] PITR 的关键约束
- 必须有完整的 binlog 链:全量备份时的 binlog position 之后所有的 binlog 都不能丢
binlog_format必须是 ROW 或 MIXED:STATEMENT 模式下 PITR 不可靠(相同的 SQL 在不同上下文中可能产生不同结果)- GTID 需要
gtid_executed一致:使用--include-gtids/--exclude-gtids要求服务端启用了enforce_gtid_consistency=ON- 恢复到某个时间点会覆盖该时间点之后的所有数据:做好评估,建议先在测试库验证恢复流程
备份验证与自动化
[!QUESTION] 思考:如果一周后发生灾难,你发现昨晚的备份根本打不开——那时候还来得及补救吗? 未经测试的备份等于没有备份。定期验证是备份策略中最重要的环节。
验证方法
# mysqldump SQL 文件验证:在测试库导入一次
mysql -u root -p test_db < backup_latest.sql
# 检查关键表的数据量和行数是否符合预期
# XtraBackup 验证:prepare 阶段就是第一次验证
xtrabackup --prepare --target-dir=/data/backups/latest/
# prepare 成功 = 备份文件完整性合格
# 更进一步:恢复到从库或测试机做一次完整演练(至少每季度一次)
自动化方案
#!/bin/bash
# /opt/scripts/mysql-backup.sh — 每日备份脚本
set -euo pipefail
BACKUP_DIR="/data/backups"
DATE=$(date +%F)
RETENTION_DAYS=7
# 1. XtraBackup 全量备份
xtrabackup --backup \
--target-dir="${BACKUP_DIR}/${DATE}/" \
--user=backup_user --password=$(cat /etc/backup_pass) \
--parallel=4
# 2. 压缩备份目录(节省磁盘空间)
tar czf "${BACKUP_DIR}/${DATE}.tar.gz" -C "${BACKUP_DIR}" "${DATE}"
rm -rf "${BACKUP_DIR}/${DATE}"
# 3. 清理超过保留期的旧备份
find "${BACKUP_DIR}" -name "*.tar.gz" -mtime +${RETENTION_DAYS} -delete
# 4. 备份元数据记录(方便追踪和恢复时定位)
echo "$(date +%F %T) | full | ${DATE} | OK" >> "${BACKUP_DIR}/backup.log"
# crontab -e — 每天凌晨 2 点执行
0 2 * * * /opt/scripts/mysql-backup.sh >> /var/log/mysql-backup-cron.log 2>&1
快速恢复清单
[!CHECKLIST] PITR 快速恢复步骤
- 确认误操作的准确时间
- 找到最新可用的全量备份及其 binlog position
- 停止应用写操作(防止二次损害)
- 恢复全量备份到干净目录
- 用
--stop-datetime重放 binlog 到误操作前- 启动 MySQL 并验证关键表数据
- 切换流量到新实例
- 通知相关人员,更新 incident 记录
备份频率建议
| 数据类型 | 全量备份频率 | 增量备份频率 | binlog 留存 |
|---|---|---|---|
| 核心交易数据 | 每天一次 | 每小时 | ≥ 7 天 |
| 用户数据 | 每天一次 | 每天 | ≥ 7 天 |
| 日志/归档数据 | 每周一次 | 按需 | 按需 |
| 测试环境 | 按需 | — | 按需 |
关联笔记
- hhs/MySQL/29-Binary Log — Binlog 是 PITR 的基础
- hhs/MySQL/30-主从复制 — 从库可以作为只读备份节点
- hhs/DEV/Go-Database — Go 应用中处理数据库恢复错误的最佳实践