MySQL 备份与恢复实战:mysqldump、XtraBackup 与 binlog 定点恢复

索引优化、事务锁、主从复制和分库分表能帮你“跑得快、扛得住”,但数据一旦误删、磁盘损坏或勒索软件加密,backup 才是最后一道防线。本文聚焦 MySQL 备份与恢复:从 mysqldump 到 XtraBackup,再到 binlog 定点恢复,把“能回滚”这件事讲透。

一、备份是最后一道防线

很多团队把备份当成“有就行”,直到出事才发现:

  • 备份文件损坏,无法解压。
  • 只备份了 schema,没备份数据。
  • 恢复演练从没做过,真正恢复时耗时半天。
  • binlog 没开,误删后只能回档到昨晚的全量。

所以备份的核心目标不只是“存一份数据”,而是在可接受的时间内,恢复到可接受的状态

二、备份分类与策略

2.1 逻辑备份 vs 物理备份

维度 逻辑备份(mysqldump) 物理备份(XtraBackup)
形式 SQL 文本 / CSV 数据文件副本
速度 慢(逐行导出) 快(拷贝页 + redo)
体积 大,可压缩 接近原库,可压缩
恢复 逐条执行 SQL 直接替换数据文件
适用 小库、跨版本、部分表 大库、生产全量、热备
一致性 --single-transaction InnoDB 热备机制

2.2 全量、增量与差异

  • 全量:每次备份完整数据。简单、恢复快,但占用空间大。
  • 增量:只备份自上次备份后变化的数据。XtraBackup 基于 LSN(Log Sequence Number)实现。
  • 差异:备份自上次全量以来的变化。介于两者之间,恢复时需要一次全量 + 一次差异。

生产常见组合:周全量 + 日增量 + 实时 binlog

2.3 热备、温备与冷备

  • 热备:应用不中断,InnoDB 可通过 XtraBackup 实现。
  • 温备:只读锁表,MyISAM 常用。
  • 冷备:停库复制数据文件,一般仅用于维护窗口。

XtraBackup 对 MyISAM 表备份时会短暂加锁,因此大量 MyISAM 的场景建议先迁到 InnoDB。

三、mysqldump 逻辑备份

3.1 最常用命令

1
2
3
4
5
6
# 全库一致性备份
mysqldump -u root -p \
--single-transaction \
--master-data=2 \
--routines --triggers --events \
--all-databases > full_backup_$(date +%F).sql

参数含义:

  • --single-transaction:在事务中导出,保证 InnoDB 一致性,不锁表。
  • --master-data=2:记录 binlog 位点,注释形式写入备份文件,便于 PITR。
  • --routines --triggers --events:导出存储过程、触发器、事件。
  • --all-databases:全库。

3.2 单库 / 单表 / 只导结构

1
2
3
4
5
6
7
8
9
10
11
# 单库
mysqldump -u root -p --single-transaction db_name > db_name.sql

# 单表
mysqldump -u root -p db_name table_name > table_name.sql

# 只导结构
mysqldump -u root -p --no-data db_name > db_name_schema.sql

# 只导数据
mysqldump -u root -p --no-create-info db_name > db_name_data.sql

3.3 压缩与定时任务

全量 SQL 通常很大,导出时直接压缩:

1
2
mysqldump -u root -p --single-transaction --all-databases \
| gzip > full_backup_$(date +%F).sql.gz

配合 crontab 每日执行:

1
0 2 * * * /usr/local/bin/backup_mysql.sh >> /var/log/mysql_backup.log 2>&1

backup_mysql.sh 示例:

1
2
3
4
5
6
7
8
9
10
11
#!/bin/bash
BACKUP_DIR=/data/backup/mysql
DATE=$(date +%F)
mkdir -p $BACKUP_DIR

mysqldump -u backup_user -p'password' \
--single-transaction --master-data=2 \
--all-databases | gzip > $BACKUP_DIR/full_$DATE.sql.gz

# 保留 7 天
find $BACKUP_DIR -name 'full_*.sql.gz' -mtime +7 -delete

密码写在命令行里会暴露在 ps 中。更安全的做法是把账号密码放进 ~/.my.cnf[mysqldump] 段,并设置 600 权限。

3.4 mydumper:并行逻辑备份

当库很大但暂时不想用物理备份时,可以用 mydumper 多线程导出:

1
mydumper -u root -p -B db_name -t 4 -o /backup/dir
  • -t 4:4 个线程。
  • 导出为多个文件,恢复用 myloader,可并发导入。

3.5 mysqldump 避坑

说明
大表导出导致从库延迟 长事务会阻塞 purge,主从场景注意选择低峰期
--single-transaction 对 MyISAM 无效 MyISAM 会锁表,应改用 --lock-all-tables
未压缩占满磁盘 大库务必备份时直接 gzip / pigz
没记录 binlog 位点 缺少位点无法做 PITR
权限不足 需 SELECT、RELOAD、LOCK TABLES、REPLICATION CLIENT

四、XtraBackup 物理备份

XtraBackup(Percona XtraBackup)是 MySQL 物理热备的首选,基于 InnoDB 崩溃恢复机制,备份期间不阻塞读写。

4.1 全量备份

1
2
3
4
5
6
7
8
9
10
# 1. 备份
xtrabackup --backup --target-dir=/backup/full/$(date +%F) \
--user=root --password='password'

# 2. 准备(应用 redo log,使数据文件一致)
xtrabackup --prepare --target-dir=/backup/full/2026-09-11

# 3. 恢复时停止 MySQL,清空数据目录后执行 copy-back
xtrabackup --copy-back --target-dir=/backup/full/2026-09-11
chown -R mysql:mysql /var/lib/mysql

4.2 增量备份

XtraBackup 增量基于 LSN:

1
2
3
4
5
6
# 周日全量
xtrabackup --backup --target-dir=/backup/full/2026-09-06

# 周一增量,基于周日全量
xtrabackup --backup --target-dir=/backup/inc/2026-09-07 \
--incremental-basedir=/backup/full/2026-09-06

恢复时,先把全量 prepare,再依次 apply 增量:

1
2
3
4
5
xtrabackup --prepare --apply-log-only --target-dir=/backup/full/2026-09-06
xtrabackup --prepare --apply-log-only --target-dir=/backup/full/2026-09-06 \
--incremental-dir=/backup/inc/2026-09-07
# 最后一天增量去掉 --apply-log-only,让它做完整恢复
xtrabackup --prepare --target-dir=/backup/full/2026-09-06

4.3 流式压缩与远程备份

大库备份直接写本地磁盘可能撑满,可用流式输出:

1
2
3
xtrabackup --backup --stream=xbstream --compress \
--user=root --password='password' \
> /backup/full.xbstream

或直传远程:

1
2
xtrabackup --backup --stream=xbstream \
| ssh backup-server "cat > /data/mysql_backup.xbstream"

4.4 mysqldump vs XtraBackup

场景 推荐工具 理由
< 50GB,需跨版本/部分表 mysqldump 灵活、可读
> 50GB,生产热备 XtraBackup 快、不锁表
需要增量/差异 XtraBackup 支持 LSN 增量
快速搭建从库 XtraBackup + --slave-info 直接复制数据目录
仅做 Schema 备份 mysqldump --no-data 文本 diff 友好

五、binlog 定点恢复(PITR)

binlog 在主从复制篇已经讲过,它是 MySQL 的逻辑变更日志。除了复制,binlog 另一个核心用途就是基于时间点的恢复(Point-in-Time Recovery, PITR)

5.1 前置条件

要做 PITR,必须同时满足:

  1. 已开启 binlog:log_bin = ON,建议 binlog_format = ROW
  2. 有全量或增量备份作为基线。
  3. 备份记录了 binlog 位点(mysqldump 的 --master-data=2 或 XtraBackup 的 xtrabackup_binlog_info)。

5.2 找到目标位点

误操作后,先别急着恢复。确定:

  • 误操作执行的大致时间。
  • 对应的 binlog 文件。
1
2
SHOW BINARY LOGS;
SHOW MASTER STATUS;

5.3 用 mysqlbinlog 恢复

假设周日凌晨 02:00 的全量备份位点是 mysql-bin.000015:156,而周三 14:30 误删了一张表。目标是把数据恢复到 14:29:59。

1
2
3
4
5
6
mysqlbinlog \
--start-position=156 \
--stop-datetime="2026-09-09 14:29:59" \
/var/lib/mysql/mysql-bin.000015 \
/var/lib/mysql/mysql-bin.000016 \
| mysql -u root -p

参数说明:

  • --start-position:从备份记录的位点开始。
  • --stop-datetime / --stop-position:恢复到误操作前一刻。
  • 可指定多个 binlog 文件,按顺序回放。

ROW 格式下,mysqlbinlog 默认输出 base64 事件。若需人工审计,可加 -v--base64-output=DECODE-ROWS 转成伪 SQL。

5.4 误删库恢复实战流程

  1. 保护现场:立即停止业务写入,避免 binlog 被覆盖。
  2. 准备临时实例:用备份恢复一个独立实例,不要直接覆盖生产。
  3. 回放 binlog:在临时实例上执行 mysqlbinlog --stop-datetime 恢复到误删前。
  4. 导出需要的数据:用 mysqldump 导出被删的库/表。
  5. 回灌生产:确认数据无误后,导入生产或做主从切换。

如果误操作是 DROP TABLE,PITR 能救回数据;但如果是 TRUNCATE TABLE 后又有新写入,目标表的数据需要单独导出再合并,不能简单回放。

5.5 binlog 恢复的常见坑

避免方法
恢复时越过误操作 精确计算 --stop-datetime,宁可少恢复几秒
多个 binlog 文件顺序错 按文件名序号依次回放
表结构变更后回放失败 确保目标实例 schema 与备份时一致
GTID 模式下重复执行 --skip-gtids--include-gtids / --exclude-gtids 精确控制

GTID 模式下,mysqlbinlog 默认带 SET @@SESSION.GTID_NEXT。若只想恢复部分事件,可用 --exclude-gtids 跳过误操作事务。

六、生产备份架构

6.1 3-2-1 原则

  • 3 份数据副本:生产数据 + 2 份备份。
  • 2 种不同介质:本地磁盘 + 对象存储 / 磁带。
  • 1 份异地:跨可用区或跨地域。

6.2 延迟从库

延迟从库是防御误删的神器:

1
CHANGE MASTER TO MASTER_DELAY = 3600;

设置 1 小时延迟后,主库误删表时,从库还有 1 小时前的数据,可直接在从库上导出恢复,省去全量 + binlog 回放的漫长等待。

6.3 备份校验与演练

备份不校验等于没备:

  • 定期恢复演练:每周/每月用备份在临时环境做一次全量恢复。
  • 校验文件完整性:对比 md5、检查压缩包能否解压。
  • 校验数据一致性:恢复后跑几条关键查询,对比记录数、checksum。
  • 监控备份任务:cron 失败告警、备份文件大小异常告警。

6.4 云 RDS 自动备份

如果使用阿里云 RDS、AWS RDS 等托管服务,通常已提供:

  • 自动全量快照。
  • 自动 binlog 备份。
  • 按时间点恢复控制台。

但即便如此,也建议额外做逻辑导出,防止云账号异常、区域故障或误操作控制台删除实例。

七、避坑清单

  • [ ] 不要只备份数据文件,忽略 binlog。
  • [ ] 不要只开 binlog,却不做全量基线。
  • [ ] 不要把备份和数据库放在同一块磁盘。
  • [ ] 不要备份到生产服务器本地就不管,至少同步到对象存储。
  • [ ] 不要高估备份文件,恢复演练要定期开展。
  • [ ] 不要给备份脚本 root 密码明文,使用专用备份账号和 .my.cnf
  • [ ] 不要忽视权限,备份账号需最小权限原则。
  • [ ] 不要在恢复时直接覆盖生产,先在临时实例验证。

总结

MySQL 备份没有银弹,但有标准答案:

  • 小库 / 跨版本 / 部分表:mysqldump。
  • 大库 / 生产热备 / 快速恢复:XtraBackup。
  • 误删回滚 / 定点恢复:全量备份 + binlog PITR。
  • 防误删兜底:延迟从库 + 异地备份。
  • 备份有效性的唯一证明:定期恢复演练。

把备份当作一项持续运营的工作,而不是一次性的脚本任务,才能在真正出事时把“删库跑路”变成“有惊无险”。