Files
cs-note/hhs/MySQL/07-高可用与分布式/34-备份与恢复.md
T
2026-05-24 11:42:38 +08:00

279 lines
9.3 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
tags: [MySQL, mysqldump, XtraBackup, PITR, 备份恢复]
create time: 2026-05-16 00:00
---
# 备份与恢复
## 概述
备份是数据安全最后一道防线。本章覆盖逻辑备份(mysqldump)、物理备份(Percona XtraBackup)、以及基于 binlog 的时间点恢复(PITR)三大核心方案。
## 备份策略全景
```mermaid
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 逻辑备份
### 全量备份
```bash
# 最基础的全量备份
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
```
### 还原
```bash
# 全量还原
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(物理备份)
### 全量备份
```bash
# 全量备份(热备,不停机)
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/
```
### 恢复
```bash
# 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 失败。
```mermaid
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 之前的状态
```
```bash
# 第一步:恢复全量备份
# (前面已讲过 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] 思考:如果一周后发生灾难,你发现昨晚的备份根本打不开——那时候还来得及补救吗?
> **未经测试的备份等于没有备份**。定期验证是备份策略中最重要的环节。
### 验证方法
```bash
# mysqldump SQL 文件验证:在测试库导入一次
mysql -u root -p test_db < backup_latest.sql
# 检查关键表的数据量和行数是否符合预期
# XtraBackup 验证:prepare 阶段就是第一次验证
xtrabackup --prepare --target-dir=/data/backups/latest/
# prepare 成功 = 备份文件完整性合格
# 更进一步:恢复到从库或测试机做一次完整演练(至少每季度一次)
```
### 自动化方案
```bash
#!/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"
```
```cron
# crontab -e — 每天凌晨 2 点执行
0 2 * * * /opt/scripts/mysql-backup.sh >> /var/log/mysql-backup-cron.log 2>&1
```
### 快速恢复清单
> [!CHECKLIST] PITR 快速恢复步骤
> 1. [ ] 确认误操作的准确时间
> 2. [ ] 找到最新可用的全量备份及其 binlog position
> 3. [ ] 停止应用写操作(防止二次损害)
> 4. [ ] 恢复全量备份到干净目录
> 5. [ ] 用 `--stop-datetime` 重放 binlog 到误操作前
> 6. [ ] 启动 MySQL 并验证关键表数据
> 7. [ ] 切换流量到新实例
> 8. [ ] 通知相关人员,更新 incident 记录
## 备份频率建议
| 数据类型 | 全量备份频率 | 增量备份频率 | binlog 留存 |
|---------|-----------|-----------|-----------|
| 核心交易数据 | 每天一次 | 每小时 | ≥ 7 天 |
| 用户数据 | 每天一次 | 每天 | ≥ 7 天 |
| 日志/归档数据 | 每周一次 | 按需 | 按需 |
| 测试环境 | 按需 | — | 按需 |
## 关联笔记
- [[hhs/MySQL/07-高可用与分布式/29-Binary Log]] — Binlog 是 PITR 的基础
- [[hhs/MySQL/07-高可用与分布式/30-主从复制详解]] — 从库可以作为只读备份节点
- [[hhs/DEV/Go-Database]] — Go 应用中处理数据库恢复错误的最佳实践