Appearance
备份与恢复
备份的目标不是在某个时间点生成一个文件——而是在需要的时候能把数据恢复到指定的状态。备份文件存在、能恢复、恢复后数据正确,这三件事要分开验证。
一、备份不是一种,是组合
常见备份方式的分工:
| 方式 | 工具 | 适合什么 | 不适合什么 |
|---|---|---|---|
| 逻辑备份 | 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 保留时间要匹配。