Skip to content

备份与恢复

备份的目标不是在某个时间点生成一个文件——而是在需要的时候能把数据恢复到指定的状态。备份文件存在、能恢复、恢复后数据正确,这三件事要分开验证。

一、备份不是一种,是组合

常见备份方式的分工:

方式工具适合什么不适合什么
逻辑备份mysqldump小库、跨版本迁移、跨环境恢复几百 GB 以上的库,恢复很慢
物理热备XtraBackup大库、追求恢复速度跨大版本恢复可能有问题
binlog 增量mysqlbinlog回放到指定时间点依赖日志连续性,不能单独当备份用
存储快照LVM/云盘快照快速备份和克隆需要处理数据库文件的一致性

常规备份策略是这样搭配的:

text
全量备份(昨晚的 mysqldump 或 XtraBackup)+ binlog(全量之后当天所有的变更)
= 能恢复到今天上午误删之前的任意时间点

关键的区分:

概念能干什么不能干什么
全量备份还原到一个完整的时间点状态只能恢复到备份完成那一刻
binlog重放备份之后的变更必须基于一个全量备份才能起步
两者配起来时间点恢复(PITR)binlog 断了、丢了就有限制

二、mysqldump——逻辑备份

备份单库:

bash
mysqldump \
  -uroot -p \
  --single-transaction \
  --routines \
  --triggers \
  --events \
  app_db > app_db.sql

参数说明:

参数干什么
--single-transaction导出前开启一个一致性读事务,InnoDB 表在导出过程中看到的是同一时刻的数据
--routines带上存储过程和函数
--triggers带上触发器
--events带上定时事件

备份所有库:

bash
mysqldump -uroot -p --all-databases --single-transaction \
  --routines --triggers --events > all.sql

仅备份某几张表:

bash
mysqldump -uroot -p app_db orders order_items > orders.sql

压缩节省空间:

bash
mysqldump -uroot -p --single-transaction app_db \
  | gzip > app_db-$(date +%F).sql.gz

--single-transaction 的"一致性"只对 InnoDB 这类事务表有效。如果库里有 MyISAM 表,它们在 mysqldump 过程中仍然可能发生变化,导致备份文件内的数据并非同一时刻的快照。所以生产库建表基本都用 InnoDB 而不是 MyISAM,备份一致性也是原因之一。

三、记录 binlog 位置——为时间点恢复做准备

备份时把当时的 binlog 位点写进 dump 文件:

bash
mysqldump -uroot -p --single-transaction --master-data=2 \
  app_db > app_db.sql

查看位点:

bash
grep -m1 'CHANGE MASTER' app_db.sql

输出里有 binlog 文件名和位置。这个位点就是全量备份对应的时间戳——恢复时,全量到这一步,binlog 从这个位点开始回放。

四、恢复 mysqldump

首先创建目标库:

sql
CREATE DATABASE app_db DEFAULT CHARACTER SET utf8mb4;

导入:

bash
mysql -uroot -p app_db < app_db.sql

压缩文件的恢复:

bash
gunzip -c app_db.sql.gz | mysql -uroot -p app_db

恢复所有库:

bash
mysql -uroot -p < all.sql

恢复前确认目标——是恢复到生产库、测试库,还是临时实例。导错目标是比备份本身失败更严重的操作。

临时实例恢复验证:

bash
mysql -h 127.0.0.1 -P 3307 -uroot -p < app_db.sql

五、binlog 回放——时间点恢复的核心步骤

先看有哪些 binlog 文件:

sql
SHOW BINARY LOGS;

查看 binlog 内容(raw 格式的人类可读版本):

bash
mysqlbinlog /data/mysql/logs/mysql-bin.000123 | less

按时间范围回放到临时实例:

bash
mysqlbinlog \
  --start-datetime='2026-05-21 10:00:00' \
  --stop-datetime='2026-05-21 10:30:00' \
  /data/mysql/logs/mysql-bin.000123 \
  | mysql -uroot -p

按位点(更精确):

bash
mysqlbinlog \
  --start-position=154 \
  --stop-position=98231 \
  /data/mysql/logs/mysql-bin.000123 \
  | mysql -uroot -p

做时间点恢复时,核对这四件事:

阶段确认什么
备份可用性全量备份能不能恢复成功,备份里 binlog 位点是否清楚
binlog 连续性从备份位点到目标时间之间,binlog 文件是否连续、完整
数据正确性恢复后关键表行数、重要数据是否符合预期
回写方案是从临时实例导出少量数据补回生产,还是整库切换

少量数据误删的典型恢复流程:

text
1. 确认误删发生的时间
2. 找到这个时间之前最近的一次全量备份
3. 在临时实例恢复全量备份
4. 回放 binlog 到误删发生前的那一刻
5. 从临时实例导出需要恢复的数据
6. 导入生产库

导出指定行的数据:

bash
mysqldump -uroot -p --where="id in (1001,1002,1003)" \
  app_db orders > restore_orders.sql

导入前看一眼 SQL 内容确认:

bash
less restore_orders.sql

六、XtraBackup——物理热备

mysqldump 导出的是 SQL 文本,上 GB 就开始吃力。几百 GB 以上的库,用 XtraBackup 做物理备份——直接复制 InnoDB 数据文件,恢复时也是文件级恢复,速度比 SQL 逐行导入快一个量级。

XtraBackup 不能像 mysqldump 那样单库单表导出——它针对的是整个实例的数据目录。版本要和 MySQL 版本匹配,8.0 的 XtraBackup 不能备份 5.7 的实例。

全量备份:

bash
xtrabackup \
  --backup \
  --target-dir=/backup/mysql/full-$(date +%F) \
  --user=backup \
  --password='BackupPassword_123!'

备份完成后需要 --prepare——应用备份期间产生的 redo,让备份目录内的数据文件变成一致可用的状态:

bash
xtrabackup \
  --prepare \
  --target-dir=/backup/mysql/full-2026-05-21

恢复时,先停 MySQL,然后拷贝回去:

bash
systemctl stop mysqld
mv /data/mysql/data /data/mysql/data.bak.$(date +%F-%H%M%S)
mkdir -p /data/mysql/data

xtrabackup --copy-back --target-dir=/backup/mysql/full-2026-05-21

chown -R mysql:mysql /data/mysql/data
systemctl start mysqld

物理恢复会覆盖整个 datadir。恢复前确认目标实例是对的——把测试恢复的命令用在生产环境上,恢复就会变成一次数据丢失事故。

七、备份账号

备份账号和普通应用账号分开:

sql
CREATE USER 'backup'@'10.0.%' IDENTIFIED BY 'BackupPassword_123!';

GRANT SELECT, SHOW VIEW, TRIGGER, EVENT, LOCK TABLES, RELOAD, PROCESS, REPLICATION CLIENT
ON *.* TO 'backup'@'10.0.%';

XtraBackup 可能需要更多权限(如 BACKUP_ADMIN),按实际版本文档确认。备份账号的好处:权限范围明确,即使备份工具或脚本被攻击,也不会泄露高权限账号。

八、备份校验和恢复演练

备份文件不是"产生了"就可靠。做校验:

bash
sha256sum app_db.sql.gz > app_db.sql.gz.sha256
sha256sum -c app_db.sql.gz.sha256

定期做恢复演练——在一个临时实例上恢复全量备份,跑几条核心查询:

sql
SHOW TABLES;
SELECT COUNT(*) FROM orders;
CHECK TABLE orders;

验证的目的是确认:备份文件能恢复、恢复后表能正常读、关键表行数和业务预期一致。备份任务成功日志不一定能证明"恢复能做"。日志说成功,文件可能损坏、字符集弄错、或有表被漏掉没导出来。

九、备份保留和清理策略

备份类型保留时间为什么
每日全量7-14 天能回到最近一两周内任意一天
每周全量4-8 周更长时间跨度的完整恢复点
每月全量6-12 月合规和审计需要
binlog覆盖全量备份间隔确保 binlog 不断链

清理前先看要删什么:

bash
find /backup/mysql -type f -mtime +30 -print

确认输出范围没问题后再真正删除:

bash
find /backup/mysql -type f -mtime +30 -delete

备份清理的节奏和恢复需求直接相关。binlog 清太快(比如只保留两天),全量备份间隔却是一周——那中间好几天无法做时间点恢复。全量频率和 binlog 保留时间要匹配。