PostgreSQL 集群异常恢复
问题现象:3节点的Patroni + PostgreSQL集群 postgresql-0 节点异常,一直处于starting状态,Lag in MB 显示 unknown,postgresql-0使用的pv、etcd集群排查都没有问题,集群状态及日志报错如下图所示,输出排查步骤及恢复方案qwen3.8-max基于您提供的截图和描述,这是一个典型的 Patroni + PostgreSQL 集群中备库(Replica)启动失败/卡住 的问题。1. 现象分析状态异常:postgresql-0 节点在 patronictl list 中显示为 starting,且 Lag in MB 为 unknown。这意味着 Patroni 无法成功连接到该节点的 PostgreSQL 进程来查询复制延迟。日志关键信息(第二张图):Still starting up as a standby.:Patroni 认为 PG 正在作为备库启动。Error communicating with PostgreSQL... /var/run/postgresql:5432 - rejecting connections:这是核心错误。PostgreSQL 进程虽然可能启动了,但它处于“拒绝连接”的状态。通常发生在 PG 正在进行崩溃恢复(Crash Recovery)、正在重放 WAL 日志尚未达到一致性状态,或者启动过程中卡住了。Failed to determine PostgreSQL state from the connection:因为连不上数据库,Patroni 无法判断其真实状态,只能回退到缓存的角色信息。拓扑结构:postgresql-2 是 Leader (TL 266),postgresql-1 是健康的 Replica (TL 135, Lag 752MB),postgresql-0 是故障节点。手动连接报错:FATAL: the database system is starting up。这证实了 PostgreSQL 进程确实活着,但它还没有完成启动/恢复过程,因此拒绝任何业务连接。这也是为什么 Patroni 报 rejecting connections 和 Lag in MB: unknown 的根本原因。进程状态 (ps -ef):PID 61 是主进程 postgres -D ...。关键进程 PID 64:postgresql: startup recovering 00000008E000000000000077。结论:您的 postgresql-0 节点当前正处于 WAL 日志回放(Crash Recovery / Standby Recovery)阶段。它正在努力应用 WAL 日志以达到一致性状态。在 startup 进程完成之前,数据库无法接受连接,Patroni 也就只能一直显示 starting。2. 为什么会卡在这里?结合您第一张图的信息:集群整体延迟大:健康的备库 postgresql-1 也有 752 MB 的延迟。这说明主库写入量很大,或者网络/磁盘 IO 存在瓶颈。Timeline (TL) 差异巨大:Leader (postgresql-2) 的 TL 是 266,而 postgresql-1 的 TL 是 135。这说明集群历史上发生过非常多次的故障切换(Failover)。当前恢复位置:postgresql-0 正在恢复的 WAL 段是 00000008E000000000000077(注意前缀 00000008 代表 Timeline 8)。核心矛盾:postgresql-0 本地的数据还停留在非常古老的 Timeline 8,而当前的 Leader 已经在 Timeline 266 上了。PostgreSQL 备库在启动时,必须先从本地 WAL 或归档中把历史日志回放完,直到追上当前的 Timeline。由于跨度太大(从 8 到 266),它可能需要回放海量的 WAL 日志,或者正在尝试寻找跨越这些 Timeline 的历史记录,导致启动过程极其漫长,看起来像是“卡住”了。3. 排错步骤请按顺序在 postgresql-0 (172.24.0.107) 节点上执行以下操作:第一步:检查 PostgreSQL 原生日志(最重要)Patroni 的日志只告诉我们“连不上”,PG 自己的日志才会告诉我们“为什么连不上”。# 找到 PG 日志路径,通常在 /var/log/postgresql/ 或数据目录下的 pg_log/ # 如果是容器化部署,使用 kubectl logs 或 docker logs tail -f /var/log/postgresql/postgresql-*.log # 或者 tail -f /home/postgres/data/pg_log/postgresql-*.log重点关注日志中的以下关键词:redo starts at ... / redo done:看是否在进行恢复。如果一直卡在 redo starts 且没有进度,说明 WAL 回放有问题。could not connect to the primary server:看是否能连通 Leader (172.24.0.116)。FATAL 或 PANIC:任何致命错误。waiting for WAL to become available:等待 WAL 日志。第二步:检查进程状态确认 PG 进程是否存在,以及处于什么状态。ps -ef | grep postgres如果没有 postgres 进程:说明 PG 根本没起来,或者是被 Patroni 反复重启。如果有进程,但状态是 D (不可中断睡眠) 或 R (运行中) 且 CPU 占用高:可能正在大量回放 WAL。检查是否有残留的 postmaster.pid:ls -l /home/postgres/data/postmaster.pid # 路径根据实际情况调整注意:不要随意删除此文件,除非你确定 PG 进程已经完全不存在。第三步:手动尝试连接(绕过 Patroni)尝试直接用 pg_isready 或 psql 连接本地 socket,看具体报错。# 检查端口是否监听 netstat -tlnp | grep 5432 # 尝试本地连接 su - postgres psql -h /var/run/postgresql -p 5432 -U postgres -c "select 1;"如果报错 the database system is starting up:证实了日志中的猜测,PG 还在恢复中,需要等待。如果报错 no pg_hba.conf entry:配置问题。如果连接直接被拒绝且无进程:PG 启动失败。第四步:检查网络连通性确保 postgresql-0 能访问 Leader (postgresql-2, 172.24.0.116) 的 5432 端口。telnet 172.24.0.116 5432 # 或 nc -zv 172.24.0.116 5432如果不通,检查防火墙、安全组或 K8s NetworkPolicy。4. 解决方案根据上述排查结果,选择对应的方案:方案 A:如果只是 WAL 回放慢(最常见)如果 PG 日志显示正在 redo 且没有报错,只是速度慢(特别是看到 pg-1 也有 700多MB 延迟,说明集群整体写入压力大或网络带宽受限):耐心等待:不要强制重启。让 PG 完成恢复。监控进度:观察日志中 LSN 的变化。优化:如果长期如此,考虑增加 max_wal_senders,调整 wal_receiver_status_interval,或检查磁盘 IO 性能。方案 B:如果 PG 进程卡死或数据损坏(推荐尝试)如果日志显示 PANIC,或者长时间(超过 30 分钟)无任何进展,且 psql 始终报 starting up,则需要重建该副本。由于是 Patroni 集群,重建非常安全。处理方案针对这种情况,有两种处理策略。强烈建议直接采用方案 B,因为等待一个跨越 200 多个 Timeline 的备库自行追平几乎是不现实的,且极易出错。方案 A:继续等待(仅适用于刚重启不久)如果您刚刚重启该节点不到 10-20 分钟,可以观察一下 PostgreSQL 的原生日志,看 LSN 是否在跳动。# 查看 PG 日志,确认是否在持续回放 tail -f /home/postgres/pgdata/pgroot/data/log/*.log # (路径根据您的实际 pg_log 位置调整)如果日志里不断有 redo at ... 且数字在变大,说明它在干活。但考虑到 TL 差距,不建议死等。方案 B:重建该副本(推荐,最快最安全)既然 PV 和 etcd 都没问题,利用 Patroni 的自动克隆功能,让它直接从当前的 Leader (postgresql-2) 重新全量同步一份最新的数据,是解决此问题的标准做法。操作步骤:1.停止 Patroni 服务为了防止 Patroni 在我们清理数据时反复尝试拉起 PG,先停掉它。# 如果是 systemd 管理 systemctl stop patroni # 如果是容器环境 (如 K8s/Docker),请通过编排工具停止该 Pod/Container2.确认并清理数据目录从截图中可以看到,您的数据目录是:/home/postgres/pgdata/pgroot/data。警告:请务必核对路径,删除错误目录会导致灾难性后果!# 再次确认路径 ls -ld /home/postgres/pgdata/pgroot/data # 清空该目录下的所有内容 (保留目录本身) rm -rf /home/postgres/pgdata/pgroot/data/* # 确认已清空 ls -A /home/postgres/pgdata/pgroot/data3.重新启动 Patronisystemctl start patroni # 或启动对应的容器/Pod4.观察重建进度Patroni 启动后,会发现数据目录为空,自动触发 pg_basebackup 从 Leader (172.24.0.116) 克隆数据。查看 Patroni 日志:tail -f /var/log/patroni/patroni.log # 或 journalctl -u patroni -f您应该会看到类似 replicating from leader、running pg_basebackup 的日志。查看集群状态:patronictl list状态变化预期:starting (正在克隆)running (克隆完成,开始流复制)Lag in MB 会从 unknown 变成一个具体的数字,并逐渐减小到 0 或很小的值。TL (Timeline) 会变成 266,与 Leader 一致。总结您遇到的不是故障,而是备库数据太旧,正在艰难地进行跨 Timeline 恢复。由于落后太多(TL 8 vs TL 266),自行恢复效率极低。清空数据目录让 Patroni 重新克隆是解决此问题的最佳实践。
以上内容来自博客订阅源,版权归原作者。热度统计站内详情页访问。

登录后,加入这场讨论。
登录参与