一、流复制故障的常见症状
当你使用PostgreSQL的流复制功能时,从库偶尔会莫名其妙地"挂起",或者突然报告"同步中断"。这时候主库还在正常干活,但从库就像断了线的风筝,数据不同步了。
典型的症状包括:
- 从库的
pg_stat_replication视图显示state变成stopped - 日志中出现"FATAL: could not receive data from WAL stream"之类的错误
- 从库的恢复进程(
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. 定期演练
每季度执行一次"复制故障演练":
- 随机停止从库的walreceiver进程
- 模拟网络中断
- 故意填满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. 文档化恢复流程
为团队准备详细的恢复手册,包括:
- 联系人名单
- 分步恢复指南
- 回滚方案
记住,在深夜被报警叫醒时,清晰的文档就是你的救命稻草!
评论