☰
KingbaseES备库启动失败?WAL损坏排查与重建全流程
2026/10/6 16:48:04 网站建设 项目流程

1. 案例背景与故障现象

1.1 生产环境里的KingbaseES集群

先交代一下背景。这套环境是某业务系统的生产数据库,用的KingbaseES V8,部署方式是经典的一主一备异步流复制。主库和备库各一台物理机,操作系统是Linux x86_64,数据库数据目录放在独立的文件系统上。业务侧有读写分离需求,平时读流量会分流一部分到备库,所以备库不光是容灾角色,还承担着实时的查询压力。

集群没有使用第三方的HA软件,直接用的数据库自带的主备复制能力,通过sys_ha或者手工配置的recovery.conf(V8版本不同略有差异)来维持主备关系。主库和备库之间走内网专线,端口和防火墙都放通了。由于是异步复制,正常情况下备库的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 top

df -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 \ --slot=standby_slot \ --wal-method=stream

这里参数逐个说明:

  • -h和-p指向主库的IP和端口。
  • -U指定用于复制的账号,这个账号需要有REPLICATION权限,不要用超级用户直接操作。
  • -D指定新备库的数据目录。
  • -R表示自动生成流复制所需的配置,包括standby.signal文件和主库连接信息。
  • --slot指定复制槽名,建议显式指定,避免自动生成乱码名称。
  • --wal-method=stream表示在备份过程中通过流复制同步WAL,而不是拷贝文件。这是最稳妥的方式,能保证备份期间WAL不丢。

执行过程看网络速度和数据量,一般几十GB需要几分钟到十几分钟。结束后检查数据目录:

ls -l /data/kingbase/data | head cat /data/kingbase/data/standby.signal cat /data/kingbase/data/postgresql.auto.conf

standby.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为streaming,replay_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 = on
could 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。

这次案例里最深的体会是:备库故障往往不是单点问题,而是多个小问题叠加的结果。归档脚本断档、复制槽保留策略过大、备库停机时间过长,单独看都不是致命的,但连在一起就让备库彻底无法恢复。运维这种事,最怕的就是“觉得备库没关系”,备库的健康程度决定了整个集群真正的可用性下限。定期做一次主备切换演练,把备库真正当成生产来维护,远比出事后再抢修更有价值。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询