一、流复制故障的常见症状

当你使用PostgreSQL的流复制功能时,从库偶尔会莫名其妙地"挂起",或者突然报告"同步中断"。这时候主库还在正常干活,但从库就像断了线的风筝,数据不同步了。

典型的症状包括:

  1. 从库的pg_stat_replication视图显示state变成stopped
  2. 日志中出现"FATAL: could not receive data from WAL stream"之类的错误
  3. 从库的恢复进程(walreceiver)不断重启
-- 检查复制状态的SQL示例 (PostgreSQL 12+)
SELECT pid, application_name, state, sync_state, 
       pg_wal_lsn_diff(sent_lsn, write_lsn) as write_lag,
       pg_wal_lsn_diff(sent_lsn, flush_lsn) as flush_lag,
       pg_wal_lsn_diff(sent_lsn, replay_lsn) as replay_lag
FROM pg_stat_replication;
/* 
pid: 复制进程ID
application_name: 从库标识名
state: 复制状态(streaming表示正常)
sync_state: 同步模式(async/quorum/sync)
各种lag值: 表示不同阶段的延迟(字节为单位)
*/

二、故障诊断三板斧

1. 先查日志准没错

PostgreSQL的日志通常能告诉你第一手线索。重点关注以下日志位置:

  • 主库:log_directory配置的路径(默认pg_log)
  • 从库:$PGDATA/pg_log目录

常见错误模式:

# 主库日志中的典型错误
2023-03-01 12:05:23 UTC [30562] FATAL:  could not send data to WAL stream: no space left on device

# 从库日志中的典型错误  
2023-03-01 12:05:24 UTC [4211] FATAL:  could not receive data from WAL stream: connection timeout

2. 网络连接检查

流复制依赖TCP连接,网络问题是最常见的故障源:

# 在从库上测试到主库的连通性 (PostgreSQL默认使用5432端口)
nc -zv master-host 5432 && echo "连接正常" || echo "连接失败"

# 更专业的做法是用pg_isready
pg_isready -h master-host -p 5432 -d postgres -U replicator

3. 检查复制槽状态

复制槽是防止主库删除从库还未接收的WAL日志的关键机制:

-- 查看主库上的复制槽状态
SELECT slot_name, active, restart_lsn, confirmed_flush_lsn,
       pg_wal_lsn_diff(restart_lsn, confirmed_flush_lsn) as lag_bytes
FROM pg_replication_slots;
/*
slot_name: 复制槽名称
active: 是否活跃
restart_lsn: 从库需要的最早WAL位置
confirmed_flush_lsn: 从库最后确认的位置
lag_bytes: 两者的差异(字节)
*/

三、五大经典故障场景及修复

场景1:磁盘空间不足

这是最"低级"但最常见的问题。当主库或从库的磁盘满了,复制就会中断。

解决方案:

# 1. 检查磁盘空间
df -h /var/lib/postgresql

# 2. 如果pg_wal目录满了,可以尝试清理旧WAL
# 注意:确保没有其他从库依赖这些WAL!
pg_archivecleanup /var/lib/postgresql/12/main/pg_wal `pg_current_wal_lsn()`

# 3. 调整wal_keep_size参数(PostgreSQL 13+)
ALTER SYSTEM SET wal_keep_size = '1GB';

场景2:网络闪断导致连接超时

不稳定的网络会导致复制连接频繁断开。

解决方案:

-- 调整主库参数(单位毫秒)
ALTER SYSTEM SET wal_sender_timeout = '60s';
ALTER SYSTEM SET tcp_keepalives_idle = '60';
ALTER SYSTEM SET tcp_keepalives_interval = '10';

-- 调整从库参数
ALTER SYSTEM SET wal_receiver_timeout = '60s';
ALTER SYSTEM SET primary_conninfo = 'host=master-host port=5432 user=replicator password=secret connect_timeout=10 keepalives=on keepalives_idle=60 keepalives_interval=10';

场景3:主从版本不一致

PostgreSQL小版本升级后,如果只升级了主库没升级从库,可能导致复制协议不兼容。

检查方法:

-- 在主库执行
SELECT version();

-- 在从库执行
SELECT version();

-- 版本差异修复方案:
1. 将主库降级到与从库相同版本(不推荐)
2. 升级从库到与主库相同版本(推荐)

场景4:从库长时间停机后无法恢复

当从库停机时间过长,主库可能已经删除了它需要的WAL日志。

解决方案:

# 1. 重建从库(最彻底的方法)
pg_basebackup -h master-host -U replicator -D /var/lib/postgresql/12/main -P -v --wal-method=stream

# 2. 或者使用pg_rewind(需要主库配合)
pg_rewind --target-pgdata=/var/lib/postgresql/12/main --source-server="host=master-host user=postgres"

场景5:配置参数不一致

某些参数必须在主从库保持一致,比如max_connections

检查方法:

-- 生成参数差异报告
SELECT name, setting 
FROM pg_settings 
WHERE name IN ('max_connections', 'shared_buffers', 'work_mem') 
ORDER BY name;

-- 修复方案:手动调整从库的postgresql.conf
ALTER SYSTEM SET max_connections = 100;  -- 与主库保持一致

四、高级修复技巧

1. 使用pg_rewind进行时间旅行

当从库远落后于主库时,pg_rewind可以神奇地将从库"回拨"到与主库一致的状态。

# 确保从库已停止
pg_ctl stop -D /var/lib/postgresql/12/main

# 执行rewind操作
pg_rewind --target-pgdata=/var/lib/postgresql/12/main \
          --source-server="host=master-host user=postgres"

# 重新创建standby.signal文件
touch /var/lib/postgresql/12/main/standby.signal

# 启动从库
pg_ctl start -D /var/lib/postgresql/12/main

2. 手动推进复制位置

在某些特殊情况下,你可能需要手动干预复制位置:

-- 在从库上查看当前恢复状态
SELECT pg_is_in_recovery(), pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();

-- 如果卡在某个LSN,可以尝试跳过(谨慎操作!)
ALTER SYSTEM SET recovery_min_apply_delay = '0';
SELECT pg_wal_replay_resume();  -- 如果处于暂停状态

3. 监控脚本示例

预防胜于治疗,这里提供一个实用的监控脚本:

#!/bin/bash
# 监控PostgreSQL复制状态的脚本

PRIMARY_HOST="master-host"
REPLICA_USER="replicator"
PORT=5432

# 检查主库复制状态
echo "主库复制状态:"
psql -h $PRIMARY_HOST -U $REPLICA_USER -p $PORT -c \
"SELECT client_addr, state, sync_state, 
 pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), sent_lsn)) as send_lag,
 pg_size_pretty(pg_wal_lsn_diff(sent_lsn, flush_lsn)) as flush_lag,
 pg_size_pretty(pg_wal_lsn_diff(sent_lsn, replay_lsn)) as replay_lag
FROM pg_stat_replication;"

# 检查从库恢复状态
echo -e "\n从库恢复状态:"
psql -U postgres -c \
"SELECT now() - pg_last_xact_replay_timestamp() AS replication_delay,
 pg_is_in_recovery(), 
 pg_last_wal_receive_lsn(), 
 pg_last_wal_replay_lsn(),
 pg_is_wal_replay_paused();"

五、预防胜于治疗

1. 监控体系建设

建议监控以下关键指标:

  • 复制延迟时间(seconds_behind_master)
  • WAL生成速率
  • 网络往返时间(RTT)
  • 磁盘空间使用率

2. 定期演练

每季度执行一次"复制故障演练":

  1. 随机停止从库的walreceiver进程
  2. 模拟网络中断
  3. 故意填满pg_wal目录
    然后练习快速恢复。

3. 参数优化建议

# postgresql.conf 最佳实践
wal_level = replica
max_wal_senders = 10
wal_keep_size = 1GB
hot_standby = on
max_standby_streaming_delay = 30s

4. 文档化恢复流程

为团队准备详细的恢复手册,包括:

  • 联系人名单
  • 分步恢复指南
  • 回滚方案

记住,在深夜被报警叫醒时,清晰的文档就是你的救命稻草!