数据库 PITR(Point-In-Time Recovery,时间点恢复)是生产环境容灾的最后一道防线。当误删表、脏数据写入或勒索软件加密发生时,仅靠”昨天晚上的全量备份”意味着至少丢失一天的数据;而掌握了 PITR,你就能把数据库精确恢复到故障发生前的任意一秒。本文用 MySQL 与 PostgreSQL 两套最主流的开源数据库,带你从原理到命令完整跑通一次时间点恢复。
一、什么是 PITR:全量备份加增量日志
PITR 的核心思想是”一份基准 + 一段日志”。先做一次全量备份作为基准点,之后数据库把每一次改动都顺序写进重做日志(MySQL 的 binlog、PostgreSQL 的 WAL)。恢复时,先还原基准备份,再按顺序”回放”日志,一直播放到你指定的那个时间点为止。因此它既能保证数据完整,又不需要每秒都做全量,是成本与安全的平衡点。相关的基础建模思路可以参考数据库设计规范实战中强调的备份与归档意识。
二、为什么不能只做全量备份
很多团队只在每天凌晨 dump 一次数据库,这其实留下了巨大的恢复窗口。下面这张表对比了三种常见策略的取舍:
| 策略 | 恢复粒度 | 数据丢失 RPO | 存储成本 | 适用场景 |
|---|---|---|---|---|
| 仅全量备份 | 天级 | 最多约 24 小时 | 低 | 可容忍丢一天数据的非核心库 |
| 全量 + 增量 | 增量间隔级 | 取决于增量频率 | 中 | 中等重要业务 |
| PITR | 秒级(任意时间点) | 接近 0 | 中高(日志需留存) | 核心交易、用户数据 |
可以看到,只有 PITR 能把 RPO 压到接近零。它的代价只是需要把日志按策略多保留几天,这对绝大多数核心库来说都值得。
三、MySQL PITR 原理:binlog 是关键
MySQL 的 PITR 依赖两种日志:redo log 保证崩溃恢复,binlog(二进制日志)记录所有数据变更,是 PITR 回放的”录像带”。要让 PITR 可用,第一步是确认 binlog 已开启且为 ROW 格式。
# 检查 binlog 是否开启
mysql -u root -p -e "SHOW VARIABLES LIKE 'log_bin';"
mysql -u root -p -e "SHOW VARIABLES LIKE 'binlog_format';"
# my.cnf 关键配置(重启生效)
[mysqld]
server-id = 1
log-bin = mysql-bin
binlog_format = ROW
binlog_row_image = FULL
expire_logs_days = 7
线上库常见基于MySQL 索引底层原理做的性能优化,但再好的索引也救不了误删数据,所以 binlog 必须常开。建议在容器化部署时把 binlog 目录挂到独立卷,避免和 data 目录抢 IO,具体可参考Docker 实战里的数据持久化章节。
四、MySQL 实战:备份与恢复到指定时间
先做一份全量备份(用 mysqldump 或物理备份工具),记录备份完成时刻,例如 2026-10-21 02:00。随后业务继续写入,binlog 持续累积。假设 09:30 有人误执行了 DROP TABLE,我们要恢复到 09:29。
# 1) 全量备份(逻辑)
mysqldump -u root -p --single-transaction --routines --triggers \
--all-databases > full_20261021_0200.sql
# 2) 把备份之后的 binlog 导出为 SQL,并截断到故障前一刻
mysqlbinlog --start-datetime="2026-10-21 02:00:00" \
--stop-datetime="2026-10-21 09:29:59" \
/var/lib/mysql/mysql-bin.000012 > increment.sql
# 3) 在干净的实例上先恢复全量,再回放增量
mysql -u root -p < full_20261021_0200.sql
mysql -u root -p < increment.sql
物理备份(如 Percona XtraBackup)能显著缩短大库的恢复时间:先 xtrabackup –prepare 得到一致性副本,再用相同思路应用 binlog 到目标时间点。无论哪种方式,关键都是 stop-datetime 要停在”坏操作”之前,否则会把误删也重放一遍。
五、PostgreSQL PITR 原理:WAL 归档
PostgreSQL 用 WAL(Write-Ahead Log)保证持久性。PITR 的做法是开启归档模式,把每段写满的 WAL 文件复制到一个安全位置,再用 pg_basebackup 做基础备份。恢复时用基础备份 + 连续 WAL 回放,直到 recovery_target_time。
# postgresql.conf 关键配置
wal_level = replica
archive_mode = on
archive_command = 'test ! -f /archive/%f && cp %p /archive/%f'
restore_command = 'cp /archive/%f %p'
recovery_target_time = '2026-10-21 09:29:59'
基础备份本身很简单,但归档命令一定要先测试目标目录可写、且不会覆盖已有文件(上面的 test ! -f 就是防止重复归档损坏链)。关于 PostgreSQL 的日常排障,可以参考PG 慢查询实战里对日志与配置的调试方法。
六、PostgreSQL 实战:基础备份与时间点恢复
# 1) 基础备份(基于标签,便于识别)
pg_basebackup -D /backup/base_20261021_0200 \
-Ft -z -P -Upostgres -h127.0.0.1 \
-c fast -l "base-20261021-0200"
# 2) 恢复:解压基础备份到新数据目录,创建恢复信号文件
tar xzf /backup/base_20261021_0200.tar.gz -C /pgdata/new
touch /pgdata/new/recovery.signal
# 在 postgresql.auto.conf 写入:
# restore_command = 'cp /archive/%f %p'
# recovery_target_time = '2026-10-21 09:29:59'
# 3) 启动实例,PG 自动回放 WAL 到目标时间后自动退出恢复模式
pg_ctl -D /pgdata/new start
恢复完成后,数据库会停留在只读的”已恢复”状态,确认数据无误后执行 SELECT pg_wal_replay_resume(); 或直接重启进入正常模式。如果不确定要恢复到哪一秒,可以先宽松地恢复到一个偏早的时间点,再用 PostgreSQL 实战里的查询手段抽样核对,逐步逼近。
七、恢复前必做的演练清单
PITR 最怕的是”平时不演练,出事才发现备份是坏的”。把下面这张清单固化成定期演练,是 DBA 的基本功:
| 检查项 | 命令/动作 | 合格标准 |
|---|---|---|
| 备份完整性 | 备份后立刻在隔离环境恢复 | 能启动且行数一致 |
| 日志连续性 | 核对 binlog/WAL 无断号 | 序列连续无缺口 |
| 恢复耗时 | 记录 RTO | 在业务容忍范围内 |
| 目标时间点 | 用 recovery_target_time 验证 | 精准停在故障前 |
| 自动化 | 备份脚本接入 CI | 失败能告警 |
把备份与恢复脚本接入 GitHub Actions 实战 的定时任务,可以让”定期演练”真正自动跑起来,而不是停留在文档里。
八、常见坑与排障
| 现象 | 根因 | 对策 |
|---|---|---|
| 恢复后数据仍缺一段 | stop-datetime 包含了坏操作 | 把时间点再往前挪几分钟重试 |
| WAL 断号无法回放 | 归档命令失败漏传 | 修复 archive_command 并重传缺失段 |
| binlog 未开启 | my.cnf 缺 log-bin | 开启后重启,历史无法 PITR |
| 恢复实例起不来 | recovery.signal 权限或路径错 | 核对数据目录属主与路径 |
还有一个隐性坑:很多团队只在主库做备份,却忘了从库的延迟可能让”最新备份”其实滞后。建议备份前先确认主从延迟(Seconds_Behind_Master / pg_stat_replication)在安全范围内。
九、把 PITR 写进运维规范
做到这里,你已经具备了跑通 PITR 的能力。下一步是把它沉淀为规范:明确 RPO(如 5 分钟)、RTO(如 1 小时),规定日志至少保留 7 天,并每季度做一次真实恢复演练。配合 VS Code 实战 里推荐的配置文件版本管理,把 my.cnf / postgresql.conf 纳入 Git,配置漂移也能追溯。
十、小结
PITR = 基准备份 + 连续日志回放,是数据库容灾的标配而非可选项。MySQL 靠 binlog、PostgreSQL 靠 WAL 归档,思路一致:先备份、留日志、恢复时停在故障前一刻。真正的可靠性来自”演练过”的备份,而不是”以为有”的备份。今天就把你的恢复演练排进日历吧——等到出事那天,它会救你一命。




