恢复实战演练:全量恢复流程、基于时间点的恢复(PITR)、误删表恢复案例

各位同学,咱们今天聊点真家伙。备份做得再好,恢复不出来,那就是一堆废纸。我见过太多团队,备份脚本写得花里胡哨,真到恢复的时候,手忙脚乱,甚至数据全丢。说白了,恢复才是备份的最终目的。

这一章,咱们就动手演练。我会带你走一遍全量恢复的标准流程,再讲讲基于时间点的恢复(PITR)怎么玩,最后用一个误删表的真实案例收尾。嗯,都是我在生产环境里踩过的坑,你拿笔记一下。

核心原则:恢复之前,先确认备份文件完整、可用。别等到跑了一半才发现备份坏了,那才叫欲哭无泪。

数据库恢复决策流程 数据库恢复决策流程 发现数据异常 是否需要 PITR? 否 全量恢复 是 PITR 恢复 恢复全量备份 应用 binlog 到目标时间 验证数据一致性 ⚠️ 恢复前务必 确认备份文件完整性

一、全量恢复流程:稳扎稳打

全量恢复,说白了就是把之前备份的完整数据文件,原封不动地倒回去。听起来简单,但步骤不能乱。我习惯按这个顺序来:

  1. 停服务 —— 先把数据库停掉,或者至少把应用连接断开。你想想看,一边恢复一边写数据,那不乱套了吗?
  2. 清数据目录 —— 把当前的数据目录清空,或者挪走。注意,别手滑删错了地方。
  3. 解压备份文件 —— 把之前打包的备份文件解压到数据目录。
  4. 修改权限 —— 确保文件属主是 mysql 用户,不然启动会报错。
  5. 启动数据库 —— 启动后检查错误日志,确认没有异常。
  6. 验证数据 —— 随便查几条记录,看看数据对不对。

我的习惯:恢复完成后,我会立刻做一次全量备份。为什么?因为恢复过程中可能产生新的日志,万一后面还要恢复,这个备份就是新的起点。

给你看个 MySQL 全量恢复的典型命令:

# 1. 停止数据库
systemctl stop mysqld

# 2. 备份当前数据目录(以防万一)
mv /var/lib/mysql /var/lib/mysql_bak

# 3. 创建新数据目录
mkdir -p /var/lib/mysql

# 4. 解压备份文件
tar -xzf /backup/full_backup_2024-01-15.tar.gz -C /var/lib/mysql/

# 5. 修改权限
chown -R mysql:mysql /var/lib/mysql

# 6. 启动数据库
systemctl start mysqld

# 7. 检查状态
mysql -e "SELECT COUNT(*) FROM information_schema.tables;"

嗯,这里要注意:如果你的备份是 mysqldump 导出的 SQL 文件,那恢复方式就不一样了。直接用 mysql < backup.sql 导入就行。但如果是物理备份(比如 XtraBackup),就得按上面的步骤来。

二、基于时间点的恢复(PITR):精确到秒

全量恢复只能恢复到备份的那个时间点。但很多时候,我们需要恢复到备份之后的某个时刻。比如,你凌晨 2 点做了备份,结果早上 10 点有人误操作删了数据。这时候,PITR 就派上用场了。

PITR 的原理,说白了就是:先恢复全量备份,再应用备份之后的 binlog,直到目标时间点。

我曾经帮一个客户恢复过数据。他们下午 3 点误删了一张核心表,全量备份是凌晨 2 点做的。我用了 PITR,精确恢复到了下午 2:59:59 的状态。嗯,数据一条没丢。

具体步骤:

  1. 恢复最近一次全量备份。
  2. 找到全量备份之后的所有 binlog 文件。
  3. 用 mysqlbinlog 工具,把 binlog 解析成 SQL,并指定停止时间点。
  4. 把解析出来的 SQL 导入数据库。

命令示例:

# 第一步:恢复全量备份(略,参考上一节)

# 第二步:查看 binlog 列表
mysql -e "SHOW BINARY LOGS;"

# 第三步:解析 binlog 到目标时间点
mysqlbinlog --stop-datetime="2024-01-15 14:59:59" \
  /var/lib/mysql/binlog.000012 \
  /var/lib/mysql/binlog.000013 \
  /var/lib/mysql/binlog.000014 \
  > /tmp/pitr_recovery.sql

# 第四步:导入恢复数据
mysql -u root -p < /tmp/pitr_recovery.sql

重要提醒:PITR 的前提是 binlog 必须完整。如果 binlog 文件损坏或丢失,那神仙也救不了。所以,binlog 的备份和全量备份一样重要。

三、误删表恢复案例:实战复盘

好了,咱们来点刺激的。假设你正在值班,突然收到报警:核心业务表 orders 被删了。你怎么办?

别慌。我经历过类似的事,当时心跳直接飙到 120。但越是这种时候,越要冷静。按步骤来:

步骤 操作 说明
1 立即锁定数据库 防止新的写入覆盖 binlog 或数据块
2 确认误删时间 从应用日志或 binlog 中找到精确时间点
3 恢复全量备份 恢复到误删之前的最近一次全量备份
4 应用 binlog 到误删前一刻 使用 --stop-datetime 精确控制
5 导出误删的表 从恢复的实例中导出 orders 表
6 导入生产环境 将表导入原数据库,注意不要覆盖其他数据

这里有个关键点:不要直接在原库上恢复。我建议另起一个实例做恢复,然后把需要的表导出来,再导入生产库。这样最安全。

给你看个实际操作的片段:

# 在另一台机器上恢复全量备份
# 假设备份文件在 /backup/full_20240115.sql
mysql -u root -p < /backup/full_20240115.sql

# 应用 binlog 到误删前一刻(假设误删时间是 10:05:00)
mysqlbinlog --stop-datetime="2024-01-16 10:04:59" \
  /backup/binlog.000015 \
  | mysql -u root -p

# 导出误删的表
mysqldump -u root -p mydb orders > /tmp/orders_recovery.sql

# 导入生产环境
mysql -u root -p mydb < /tmp/orders_recovery.sql

避坑指南:我曾经遇到过一次,binlog 里混杂了其他库的 DDL 语句,导致恢复时把别的表结构也改了。所以,用 mysqlbinlog 时,最好加上 --database=mydb 参数,只恢复指定库的日志。

四、恢复后的验证:别急着走

数据恢复完了,不代表万事大吉。我见过有人恢复完就跑,结果第二天发现数据对不上。嗯,验证这一步,不能省。

我一般会做这几件事:

  • 行数对比 —— 用 SELECT COUNT(*) 对比恢复前后的记录数。
  • 关键业务数据抽查 —— 找几条最近的订单,核对金额、状态。
  • 检查自增 ID —— 有时候恢复会导致自增 ID 错乱,需要手动调整。
  • 通知业务方验证 —— 让业务同事跑几个核心功能,确认没问题。

我的习惯:恢复完成后,我会立刻做一次全量备份,并保留恢复过程中的所有日志。这样,万一后续发现问题,还能回溯。

好了,这一章的内容就到这里。全量恢复、PITR、误删表恢复,这三个场景基本覆盖了日常工作中 90% 的恢复需求。你回去之后,最好在测试环境里亲手演练几遍。只有亲手做过,心里才有底。