一次深夜告警复盘:我们是如何用pg_basebackup + 归档搞定PostgreSQL备库WAL丢失的
深夜告警:PostgreSQL备库WAL同步中断的实战救援记录
凌晨2:17,手机突然响起刺耳的告警铃声。监控系统显示生产环境的PostgreSQL备库同步状态异常,pg_stat_replication视图中的replay_lag字段已经飙升至3小时以上。作为当晚的值班工程师,我立刻从床上弹起来,打开笔记本开始排查这个突如其来的故障。
1. 故障现象与初步诊断
登录备库服务器后,首先检查PostgreSQL日志,发现了关键错误信息:
2024-03-15 02:05:23.417 UTC [28761] FATAL: could not receive data from WAL stream: ERROR: requested WAL segment 0000000100000000000000AB has already been removed这个错误表明备库请求的WAL日志段在主库上已经被清理。进一步查看主库的pg_wal目录,确认确实缺少从0000000100000000000000AB开始的连续几个WAL文件。
关键排查步骤:
检查主备库的WAL位置差异:
-- 在主库执行 SELECT pg_current_wal_lsn(); -- 在备库执行 SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();确认主库的WAL保留配置:
SHOW wal_keep_segments; SHOW max_wal_size;查询最近的大型事务:
SELECT pid, usename, application_name, backend_start, xact_start, query_start, state_change, wait_event_type, wait_event, state, backend_xmin, query FROM pg_stat_activity WHERE backend_type = 'client backend' ORDER BY xact_start DESC;
排查发现,当晚23:00左右有一个批量数据导入作业,处理了近500万条记录。这个长时间运行的事务产生了大量WAL日志,而备库由于网络带宽限制未能及时同步。
2. 应急决策:为什么选择pg_basebackup重建
面对WAL缺失的情况,通常有几种恢复方案:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 增大wal_keep_segments | 简单快速 | 治标不治本,可能再次出现同样问题 | 临时应急,测试环境 |
| 使用归档日志恢复 | 可靠,能精确恢复 | 需要提前配置好归档 | 已启用归档的环境 |
| pg_basebackup重建 | 彻底解决问题 | 耗时较长,需要停机 | 数据量适中,允许维护窗口 |
| 启用复制槽 | 预防性措施 | 需要提前配置 | 长期解决方案 |
我们环境已经配置了WAL归档,但考虑到:
- 缺失的WAL段较多,从归档恢复耗时可能超过维护窗口
- 备库本身没有承担关键读负载
- 需要彻底解决类似问题
最终决定采用pg_basebackup重建备库+归档追平的组合方案。这个选择基于以下考量:
- 虽然重建需要时间,但能确保数据一致性
- 归档日志可以保证不丢失任何数据变更
- 重建过程可以并行进行,减少停机时间
3. 详细恢复操作流程
3.1 准备工作
首先确保主库归档功能正常:
# 检查归档状态 psql -h primary -U postgres -c "SELECT name, setting FROM pg_settings WHERE name LIKE '%archive%';" # 手动触发WAL切换并检查归档 psql -h primary -U postgres -c "SELECT pg_switch_wal();" ls -l /archive/path/准备备库重建环境:
# 停止备库服务 sudo systemctl stop postgresql-12 # 备份重要配置文件 cp /var/lib/pgsql/12/data/postgresql.conf /tmp/ cp /var/lib/pgsql/12/data/recovery.conf /tmp/ 2>/dev/null || echo "No recovery.conf"3.2 使用pg_basebackup获取基础备份
在主库上创建专用复制用户(如尚未配置):
CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD 'securepassword'; ALTER ROLE replicator CONNECTION LIMIT 3;在备库节点执行基础备份:
# 清空数据目录(先确认备份了配置文件) rm -rf /var/lib/pgsql/12/data/* # 执行基础备份 pg_basebackup -h primary -U replicator -p 5432 -D /var/lib/pgsql/12/data \ -P -v -R -X stream -c fast -C -S standby1 # 恢复配置文件 cp /tmp/postgresql.conf /var/lib/pgsql/12/data/关键参数说明:
-R:自动创建recovery.conf(PG12+集成到postgresql.conf)-X stream:在备份期间同时流式传输WAL-c fast:使用快速检查点-C -S standby1:同时创建复制槽,避免未来WAL丢失
3.3 配置归档恢复
编辑postgresql.conf配置归档恢复:
restore_command = 'cp /archive/path/%f "%p"' archive_cleanup_command = 'pg_archivecleanup /archive/path %r' recovery_target_timeline = 'latest'对于PostgreSQL 12及以上版本,配置standby.signal文件:
touch /var/lib/pgsql/12/data/standby.signal3.4 启动备库并监控进度
启动备库服务:
sudo systemctl start postgresql-12实时监控恢复进度:
tail -f /var/lib/pgsql/12/data/log/postgresql-$(date +%a).log # 在备库查询恢复状态 psql -c "SELECT pg_is_in_recovery(), pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn(), pg_wal_lsn_diff( pg_current_wal_lsn(), pg_last_wal_replay_lsn()) AS lag_bytes;"4. 预防措施与经验总结
这次故障的根本原因是缺乏对大型事务的监控和防护机制。事后我们实施了以下改进:
复制槽标准化配置:
-- 主库创建永久复制槽 SELECT * FROM pg_create_physical_replication_slot('standby1_slot'); -- 备库配置 primary_slot_name = 'standby1_slot'大事务监控系统:
-- 部署监控脚本,检测长时间运行的事务 SELECT pid, now() - xact_start AS duration, query FROM pg_stat_activity WHERE state IN ('idle in transaction', 'active') AND now() - xact_start > interval '5 minutes';WAL归档增强:
- 实现多级归档保留策略(本地7天+S3 30天)
- 部署定期归档完整性检查脚本
备库同步告警优化:
- 设置多级阈值告警(1小时警告,3小时严重)
- 增加备库延迟与主库WAL保留量的关联检查
关键教训:
- 对于批量操作,应该拆分为小事务分批提交
- 生产环境必须配置复制槽或足够的WAL保留
- 定期验证归档完整性和恢复流程
- 维护文档化的应急操作手册
这次深夜故障处理让我深刻体会到,数据库高可用不是简单的配置问题,而是需要从架构设计、监控预警到应急响应全链条的周密考虑。特别是对于PostgreSQL这类复杂系统,理解其WAL机制和复制原理,才能在关键时刻做出正确的决策。
