1. 案例背景与故障现象1.1 生产环境里的KingbaseES集群先交代一下背景。这套环境是某业务系统的生产数据库用的KingbaseES V8部署方式是经典的一主一备异步流复制。主库和备库各一台物理机操作系统是Linux x86_64数据库数据目录放在独立的文件系统上。业务侧有读写分离需求平时读流量会分流一部分到备库所以备库不光是容灾角色还承担着实时的查询压力。集群没有使用第三方的HA软件直接用的数据库自带的主备复制能力通过sys_ha或者手工配置的recovery.confV8版本不同略有差异来维持主备关系。主库和备库之间走内网专线端口和防火墙都放通了。由于是异步复制正常情况下备库的WAL接收和回放会稍微滞后于主库但这个滞后通常在毫秒级业务完全无感。1.2 故障是怎么被发现的那天下午接到值班同事反馈说监控平台报了一条告警备库的数据库进程状态异常具体表现为kingbase进程不存在。第一反应是有点意外因为主库一切正常所有业务读写都没受影响监控面板上主库的QPS、连接数、慢查询都平稳。也就是说这个故障是“静默”的——业务没有感知但备库已经悄悄挂了。我上备库服务器看了一眼确实ps -ef | grep kingbase一个进程都没有。再尝试手动启动备库实例输入sys_ctl start后终端只回了一句“could not start server”随后去看数据库日志报错信息很典型database system was interrupted while in recovery at ...后面还跟着一段invalid record length at ...。这个场景相信跑过PostgreSQL系数据库的DBA都不陌生——备库在启动时进入了恢复流程但恢复进度被卡住无法继续。主库正常、备库无法启用这在主备集群里是最容易被忽略的一类问题。因为业务没挂很多团队会想“反正有主库顶着备库慢慢修”但问题是备库长期不恢复一旦主库真出了问题整个系统就连容灾兜底都没有了。这篇文章就把这次排查和修复的完整过程写出来包括原理、命令、坑希望能给遇到同类问题的人一个参考。2. 备库启动失败的原理与常见诱因2.1 备库启动时到底在跑什么流程想搞明白备库为什么起不来得先知道备库启动时做了什么。KingbaseES的流复制机制与PostgreSQL大体一致备库启动后会先读取数据目录下的pg_control文件这个文件相当于数据库恢复的“书签”里面记录了当前数据库的时间线和最近一个检查点的位置。拿到“书签”之后备库进入恢复模式开始从WAL日志里读取并重放变更。如果配置的是流复制备库还会尝试连接主库通过复制协议拉取主库生成的新WAL段。整个启动过程就像一个人拿着半本小说想接着往下读但前提是手里得有后续章节。备库启动失败本质就是“手里的书签坏了”或者“后续章节找不到了”。具体到这次案例报错中的database system was interrupted while in recovery at ...意思是上一次停止时数据库正处于恢复中这个状态本身常见正常关机或闪断都可能留下这个记录。但后面的invalid record length at ...说明备库在某个WAL段里读到了一个长度非法的记录恢复进程无法解析这个WAL记录于是一遍遍重试最终启动失败。2.2 备库无法启用的五大类根因根据我的经验备库启动失败基本逃不出下面这几类。排查时可以按这个框架逐一对照能省很多时间。**第一类是文件和权限问题。**备库数据目录属主不是kingbase用户或者关键文件被误删比如误删了pg_control、standby.signal、postgresql.conf这些都可能导致启动失败。权限问题常见于用root人为干预过数据目录或者从备份恢复时没有保持属主。**第二类是磁盘与系统资源问题。**备库所在的文件系统满了或者/tmp分区不够数据库启动时写不了临时文件也会起不来。还有内存参数配置过大超过了实际物理内存操作系统OOM直接把启动进程杀掉表现就是启动失败。**第三类是WAL和恢复状态问题。**这类是最难排查的。备库的pg_wal老版本叫pg_xlog目录下缺少对应的WAL段或者某个WAL段文件损坏、被截断恢复进程读到一半就卡住了。另外如果备库曾被人为以主库方式启动过时间线已经推进原来的恢复路径就失效了这种现象叫时间线分歧。**第四类是复制槽Replication Slot异常。**如果配置了复制槽备库会依赖主库上对应的槽位来保留WAL。备库启动时会要求主库发送指定位置的WAL一旦主库上的复制槽状态异常、槽位丢失或者活跃状态卡住备库可能一直拿不到需要的数据。**第五类是集群管理软件或配置问题。**比如备库配置里primary_conninfo写错了IP或端口或者standby.signal文件丢失备库被当成独立主库启动会尝试做普通恢复而不是流复制恢复导致启动行为完全不符合预期。这次故障其实同时踩了第三类和第五类的雷后面细说。3. 一步步排查从现象到定位3.1 第一道检查进程、端口与服务状态拿到现场后我先按从外向里的顺序做基础检查。第一件事是确认备库进程真的没了用下面几条命令ps -ef | grep kingbase ss -lntp | grep 54321正常情况应该看到kingbase主进程、syslogger、checkpointer、walreceiver备库特有等进程。如果ss输出里54321端口没有监听说明数据库实例确实没起来。接着看系统整体负载和磁盘df -h free -g topdf -h重点看数据目录所在分区free -g看内存。因为异步复制备库通常和主库的配置参数一致如果主库能跑、备库内存比主库小很容易触发OOM。我见过不止一次因为备库内存不足导致kingbase进程被系统杀掉的情况。这次检查下来磁盘和内存都还正常。然后尝试用数据库自带命令启动注意一定要用kingbase用户执行su - kingbase sys_ctl -D /data/kingbase/data start启动后立刻看日志如果起不来日志会给出第一手线索。KingbaseES的日志默认在数据目录下的sys_log目录里文件名类似startup.log或postgresql-*.log也可以用下面的命令实时跟踪tail -f /data/kingbase/data/sys_log/startup.log这一步是排查的核心起点。日志里会直接告诉你失败在哪一步比任何猜测都准。3.2 读懂日志里的关键报错这次日志里最扎眼的还是那句FATAL: database system was interrupted while in recovery at 2025-06-10 14:23:45 CST LOG: invalid record length at 0/1A2B3C0: wanted 24, got 0逐词拆解一下。database system was interrupted while in recovery表明备库上次停止时正处于恢复过程中这个现象可能是备库非正常断电、进程被强杀或者上次启动恢复过程中又被中断导致的。数据库本身认为这次启动需要继续恢复于是开始扫描WAL。invalid record length at ...则是恢复过程的直接死因。备库在一个WAL记录的起点读取记录头记录头的固定长度是24字节。但它读到的长度不对可能是0也可能是负数或超大值。got 0表示在指定位置读不到任何数据也就是WAL文件在该位置后是缺失的或者整个文件是空的、被截断的。看到这里我基本可以断定问题出在WAL上。但还不清楚是单个WAL段损坏还是整个数据目录已经不一致。3.3 文件权限与数据目录体检先做一遍文件层级的体检。用这些命令ls -ld /data/kingbase/data ls -l /data/kingbase/data/pg_control ls -l /data/kingbase/data/pg_wal一般生产库的数据目录属主都应该是kingbase:kingbase。如果发现权限不对直接用chown -R kingbase:kingbase修正。同时注意pg_wal目录里WAL段文件的权限和属主。这次检查时权限一切都正常。再看pg_control文件信息可以用KingbaseES自带的工具sys_controldata /data/kingbase/data输出里会包含Latest checkpoint location、Prior checkpoint location、Database cluster state等关键信息。如果Database cluster state显示in production说明这个备库的数据目录可能被当成主库启动过这是个危险信号。如果显示shut down或in archive recovery就还在正常的备库状态范围。当时我看到的状态是in archive recovery说明数据库确实是按备库模式启动的但恢复仍然失败。于是把注意力放到WAL段文件上。3.4 归档与WAL状态核实因为这套环境配置了WAL归档先去归档目录看看最近几个WAL段有没有断档ls -l /data/archive_wal | tail -20流复制备库启动时优先会去主库拉取WAL拉不到就从本地pg_wal和归档目录找。如果归档目录连续、本地pg_wal也有对应文件那问题很可能出在文件内容损坏而不是缺失。再看主库上对应的复制槽状态登录主库执行SELECT slot_name, slot_type, active, restart_lsn, confirmed_flush_lsn FROM pg_replication_slots;如果备库对应的槽位显示active false可能是备库断连太久主库上的槽位已经失效。如果restart_lsn远大于备库当前需要的位置那就意味着备库要请求的WAL段已经被主库清理掉了需要走重建流程。这次查下来主库上的复制槽还在但restart_lsn已经比备库卡住的位置大了不少说明备库自己离线了太长时间中间累积的WAL段无法完整找回。3.5 归档不完整的确认进一步核查归档目录发现了一个关键问题归档WAL有断档。某个时间段的WAL段序列不连续中间少了好几个文件。可能是当时归档脚本卡住或者归档目录被手工清理过。这个断档点正好和备库日志里报invalid record length的位置对应上。也就是说备库在恢复过程中需要的WAL段在主库和备库本地都找不到了归档也不完整整个恢复路径在这里断裂。主库当然不受影响因为它不需要回放历史WAL备库却卡在这个断点上怎么都无法继续。到这里根因基本清晰备库数据目录的恢复起点已经落后于可用WAL的范围不可能通过简单的“补WAL”方式拉起来。最实际、最干净的做法是直接重建备库。4. 本次案例的根因和修复过程4.1 为什么主库正常备库就起不来很多人会有这个疑惑主备之间是有复制关系的主库好好的备库为什么不能自动跟上关键在于复制是单向的。备库要恢复需要从自己的恢复起点往后读取WAL而WAL的来源包括本地pg_wal、归档目录以及实时从主库拉取。主库不会主动把未来或者缺失的WAL“塞”给备库它只会在备库请求某个WAL段时发送对应内容。如果备库请求的起点已经超过了主库能提供的历史范围复制就无法继续。打个比方主库是一个每天坚持写日记的人备库是一个负责抄写日记的人。日记本某几页被撕掉了抄写员手里最新的页数是昨天的他开始请求“请给我前面缺的那几页”但主库说“那几页早就没有了我这里只有今天的”。于是抄写员只能停在那里无法继续。这种情况下靠配置调整是救不回来的。唯一的办法是让备库从头开始从主库拿一个完整的数据快照然后重新建立复制关系。4.2 重建备库前的关键备份与准备重建备库前有两个事必须做。第一确认主库数据目录完整备份文件系统没问题。第二把备库现有数据目录做一份归档不要直接删。虽然数据不一致但保不齐后续需要做日志分析。具体操作是先把备库的进程彻底停掉检查没有残留的kingbase进程然后重命名数据目录su - kingbase sys_ctl -D /data/kingbase/data stop mv /data/kingbase/data /data/kingbase/data_broken_$(date %Y%m%d) mkdir /data/kingbase/data为什么用mv而不是rm因为数据库数据目录动辄几百GB直接删除可能触发日志丢失、磁盘IO抖动而且在根因没有完全确认前保留现场是运维的基本素养。重建完成后确认新实例正常再考虑清理旧目录。接下来创建新的数据目录。KingbaseES提供了专门的备份工具可以生成一个一致性的备库基础备份。命令示例/opt/kingbase/ES/V8/bin/sys_basebackup \ -h 192.168.1.10 \ -p 54321 \ -U replica \ -D /data/kingbase/data \ -R \ --slotstandby_slot \ --wal-methodstream这里参数逐个说明-h和-p指向主库的IP和端口。-U指定用于复制的账号这个账号需要有REPLICATION权限不要用超级用户直接操作。-D指定新备库的数据目录。-R表示自动生成流复制所需的配置包括standby.signal文件和主库连接信息。--slot指定复制槽名建议显式指定避免自动生成乱码名称。--wal-methodstream表示在备份过程中通过流复制同步WAL而不是拷贝文件。这是最稳妥的方式能保证备份期间WAL不丢。执行过程看网络速度和数据量一般几十GB需要几分钟到十几分钟。结束后检查数据目录ls -l /data/kingbase/data | head cat /data/kingbase/data/standby.signal cat /data/kingbase/data/postgresql.auto.confstandby.signal存在说明这个实例会被当作备库启动postgresql.auto.conf里应该有主库的连接信息。确认无误后用sys_ctl start启动备库。4.3 启动验证与复制状态确认启动后立刻看日志tail -f /data/kingbase/data/sys_log/startup.log正常会看到类似这样的日志LOG: database system was interrupted while in recovery at ... LOG: entering standby mode LOG: redo starts at ... LOG: consistent recovery state reached LOG: started streaming WAL from primary at ...最关键的信号是最后一句——已经开始从主库接收WAL流。这时表明备库基本恢复健康。接着在备库上执行查询确认复制延迟SELECT pg_is_in_recovery(); SELECT now() - pg_last_xact_replay_timestamp() AS replay_lag;第一条返回true说明仍然处于恢复模式第二条返回的时间差就是回放延迟。通常应该在秒级以内。同时到主库上确认备库已经连接SELECT client_addr, state, sync_state, sent_lsn, replay_lsn FROM pg_stat_replication;看到state为streamingreplay_lsn在持续增长说明主备复制已经恢复正常。这次重建后我特意观察了半小时备库日志没有再出现invalid record length主备延迟保持在0到1秒之间业务侧的只读流量也已经正常分流到备库。5. 备库无法启用的避坑指南与预防措施5.1 常见问题速查表结合之前的处理经验把备库无法启用的情况整理成一张速查表。遇到问题时可以先按表排查能快速定位大多数常见故障。现象可能原因快速解决办法日志报权限不足启动直接失败数据目录属主不是kingbasechown -R kingbase:kingbase /data/kingbase启动后进程立刻消失日志无报错内存不足被OOM调低shared_buffers加大交换空间或扩容invalid record length at ...WAL段文件损坏或缺失检查归档和pg_wal确认断点必要时重建备库hot standby is not possible because ...备库参数hot_standby未开启在postgresql.conf中设置hot_standby oncould not receive data from WAL stream主备网络异常或主库max_wal_senders不足排查网络增加max_wal_senders重启备库replication slot ... is active备库已死复制槽仍显示active在主库执行pg_drop_replication_slot后重建time line mismatch备库被误以主库方式启动过重建备库不要尝试手工修改时间线pg_control文件缺失或损坏文件系统坏块或人为删除如果备份中有该文件可恢复否则只能重建实例今天这个案例属于第二行中“WAL段缺失”的范畴但表象比较隐蔽因为主库完全正常所以更容易被忽略。5.2 日常运维最该盯紧的五个指标备库出问题不可怕可怕的是没人发现。经历过这次故障之后我强烈建议大家在监控体系里加入下面五类指标全部都有对应的动态视图可以直接查询。第一备库进程是否存活。这个最简单监控端口或者pg_isready命令都可以。但要注意进程存活不等于复制正常一定要配合下面的指标。第二复制延迟。用pg_last_xact_replay_timestamp()和当前时间对比超过阈值就告警。异步复制环境里延迟突然拉大往往意味着备库回放卡住。第三主库上的复制槽状态。查询pg_replication_slots关注restart_lsn是否过期。如果复制槽不需要了及时删除避免WAL堆积撑爆磁盘但删除前必须确认备库已经重建完成。第四归档目录的连续性和保留时间。归档是备库恢复的保险丝断档比没有归档更隐蔽。建议每天做一次归档完整性检查至少确保最近3天的WAL段是连续的。第五备库的pg_control状态。可以每周跑一次sys_controldata确认数据库集群状态始终是in archive recovery或shut down不要变成in production。一旦变成后者说明备库可能被错误激活过。5.3 处理同类故障的通用排查思路最后把这次处理的思路总结成一个可复用的排查套路以后遇到“备库起不来”的问题按这个顺序走大概率不会跑偏。第一步看日志。不要一上来就重建日志里的第一句FATAL往往就是答案。不管是invalid record length还是could not open file先顺着报错去查。第二步确认基础资源。磁盘是否写满内存是否不足端口是否被占用数据目录权限是否正确。这些检查通常一分钟内能出结果能排除一大半问题。第三步评估WAL连续性。查看本地pg_wal和归档目录定位备库当前需要的位置看看主库上对应的WAL还有没有、复制槽还在不在。如果这个过程发现断档大概率要重建。第四步尝试标准恢复手段。如果WAL只是暂时不可用可以把备库停在安全状态从主库重新拉取。如果已经出现物理损坏且无法通过pg_wal补齐那就别犹豫直接重建。第五步重建前保留现场重建后验证复制。保留旧数据目录重建不漏掉复制参数启动后确认pg_stat_replication里备库已经变为streaming。这次案例里最深的体会是备库故障往往不是单点问题而是多个小问题叠加的结果。归档脚本断档、复制槽保留策略过大、备库停机时间过长单独看都不是致命的但连在一起就让备库彻底无法恢复。运维这种事最怕的就是“觉得备库没关系”备库的健康程度决定了整个集群真正的可用性下限。定期做一次主备切换演练把备库真正当成生产来维护远比出事后再抢修更有价值。
阅读完成 · 觉得有帮助?