--- tags: [MySQL, mysqldump, XtraBackup, PITR, 备份恢复] create time: 2026-05-16 00:00 --- # 备份与恢复 ## 概述 备份是数据安全最后一道防线。本章覆盖逻辑备份(mysqldump)、物理备份(Percona XtraBackup)、以及基于 binlog 的时间点恢复(PITR)三大核心方案。 ## 备份策略全景 ```mermaid flowchart LR subgraph "逻辑备份" LD["mysqldump
导出 SQL"] LF["全量 + 增量
依赖 binlog"] end subgraph "物理备份" PD["Percona XtraBackup
热拷贝ibd文件"] PF["压缩 + 加密
支持部分备份"] end subgraph "选择指南" LS["数据量 < 50GB
停机窗口可接受"] PS["数据量 > 50GB
需要 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 应用中处理数据库恢复错误的最佳实践