PostgreSQL 因主库表空间错乱_缺少软链接、从库配置问题,导致主从复制故障【修复过程】

修复后目录结构
bash 复制代码
/var/lib/pgsql/15/
├── data/                          # PGDATA 主目录
│   ├── pg_tblspc/
│   │   └── 24610 -> /var/lib/pgsql/15/pg_tps    # 符号链接
│   ├── base/                      # 默认表空间数据
│   ├── global/                    # 系统表
│   └── ...其他系统目录
├── pg_tps/                        # 表空间 mydemotbs 的实际存储位置
│   └── PG_15_202209061/
│       ├── 16388/                 # 某个数据库的数据
│       └── 24847/                 # db_666 的数据
└── archive/                       # WAL 归档

一、前提:

bash 复制代码
pg_basebackup -h 192.168.x.x -U rep -D /var/lib/pgsql/15/data -Fp -Xs -P -R
  • 执行以上命令从主库拉取数据,搭建主从复制总是失败。
从库日志报错:
bash 复制代码
2026-07-07 20:00:46 CST [13082]: user=,db=,app=,client=LOG:  database system was interrupted; last known up at 2026-07-07 19:53:31 CST
cp: cannot stat '/var/lib/pgsql/15/archive/00000011.history': No such file or directory
2026-07-07 20:00:46 CST [13082]: user=,db=,app=,client=FATAL:  recovery target timeline 17 does not exist

二、根据日志报错先排查修复主库报错

主库日志报错:
bash 复制代码
2026-07-07 17:38:57 CST [17670]: user=,db=,app=,client=DETAIL:  The database subdirectory "pg_tblspc/24610/PG_15_202209061/24847" is missing.
排查过程:
  • 现象:表空间文件 /var/lib/pgsql/15/pg_tps 和 /var/lib/pgsql/15//var/lib/pgsql/15/ 之间缺少软链接,且各个表空间文件下都有重复数据,后续会导致严重问题。
1、停止主库
bash 复制代码
systemctl stop postgresql-15
2、删除各表空间目录下多余文件和目录,只保留数据最全的表空间文件
3、根据日志报错,进入数据目录创建软链接:
bash 复制代码
cd /var/lib/pgsql/15/data
ln -s /var/lib/pgsql/15/pg_tps pg_tblspc/24610

# 软链接结果:
$ ll data/pg_tblspc
lrwxrwxrwx 1 postgres postgres 24 Jul  7 19:39 24610 -> /var/lib/pgsql/15/pg_tps
4、重启主库验证:
bash 复制代码
systemctl start postgresql-15

# 查看日志
tail -50 /var/lib/pgsql/15/data/log/*.log

# 验证 db_666
psql -U postgres -c "\c db_666"
psql -U postgres -c "SELECT count(*) FROM pg_tables WHERE schemaname = 'public'

三、修复从库:

1、从库同目录创建表空间文件
bash 复制代码
mkdir -p /var/lib/pgsql/15/pg_tps
2、清理从库旧数据
bash 复制代码
rm -rf /var/lib/pgsql/15/data/*
rm -rf /var/lib/pgsql/15/archive/*
3、从主库拉取数据
bash 复制代码
pg_basebackup -h 192.168.x.x -U rep -D /var/lib/pgsql/15/data -Fp -Xs -P -R
4、修改postgresql.conf
bash 复制代码
# 设置为空:
restore_command = ''
5、重启从库报错:
bash 复制代码
systemctl start postgresql-15.service
从库日志报错:
bash 复制代码
2026-07-07 20:00:46 CST [13082]: user=,db=,app=,client=LOG:  database system was interrupted; last known up at 2026-07-07 19:53:31 CST
cp: cannot stat '/var/lib/pgsql/15/archive/00000011.history': No such file or directory
2026-07-07 20:00:46 CST [13082]: user=,db=,app=,client=FATAL:  recovery target timeline 17 does not exist
6、排查过程
1. 查看主库当前的 timeline
bash 复制代码
# 在从库执行
# 1. 查看 postgresql.auto.conf 内容
[postgres@pg05 data]$ cat postgresql.auto.conf
l# Do not edit this file manually!
# It will be overwritten by the ALTER SYSTEM command.
checkpoint_completion_target = '0.9'
primary_conninfo = 'user=rep password=123456 channel_binding=prefer host=192.168.6.9 port=5432 sslmode=prefer sslcompression=0 sslsni=1 ssl_min_protocol_version=TLSv1.2 gssencmode=prefer krbsrvname=postgres target_session_attrs=any'

# 2. .conf 里是否有 restore_command
grep "restore_command" postgresql.conf
grep "recovery_target_timeline" postgresql.conf

# 3. 查看 standby.signal 是否存在
[postgres@pg05 data]$ ls -la standby.signal
-rw-r----- 1 postgres postgres 0 Jul  7 19:54 standby.signal

# 4. 查看 recovery.signal(PG12+ 如果有的话)
[postgres@pg05 data]$ ls -la recovery.signal
ls: cannot access recovery.signal: No such file or directory

# 5. 检查 postgresql.conf 里是否有 restore_command
[postgres@pg05 data]$ grep "restore_command" postgresql.conf
restore_command = ''		# command to use to restore an archived logfile segment
[postgres@pg05 data]$ grep "recovery_target_timeline" postgresql.conf
#recovery_target_timeline = 'latest'	# 'current', 'latest', or timeline ID
bash 复制代码
# 1. 查找所有包含 restore_command 的配置文件
[postgres@pg05 data]$ grep -r "restore_command" /var/lib/pgsql/15/data/ 2>/dev/null

postgresql.conf 中 restore_command 的实际生效值
psql -U postgres -c "SHOW restore_command;" 2>/dev/null || echo "无法连接"

/var/lib/pgsql/15/data/pg_rman_recovery.conf:restore_command = 'cp /var/lib/pgsql/15/archive/%f %p'
/var/lib/pgsql/15/data/postgresql.conf-bak20260706:
#restore_command = ''		# command to use to restore an archived logfile segment
/var/lib/pgsql/15/data/postgresql.conf-bak20251025:
#restore_command = ''		# command to use to restore an archived logfile segment
/var/lib/pgsql/15/data/postgresql.conf:
restore_command = ''		# command to use to restore an archived logfile segment

# 2. 检查是否使用了 include 指令引入了外部配置
[postgres@pg05 data]$ grep "^include" postgresql.conf
include = 'pg_rman_recovery.conf' # added by pg_rman 1.3.15

# 3. 查看 postgresql.conf 中 restore_command 的实际生效值
[postgres@pg05 data]$ psql -U postgres -c "SHOW restore_command;" 2>/dev/null || echo "无法连接"
结果:无法连接
根本原因:
bash 复制代码
# postgresql.conf 里有一行:
include = 'pg_rman_recovery.conf' # added by pg_rman 1.3.15

# 而这个 pg_rman_recovery.conf 文件里设置了:
restore_command = 'cp /var/lib/pgsql/15/archive/%f %p'

这就是问题所在! 虽然你在 postgresql.conf 里设置了 restore_command = '',但 include 指令在后面引用了 pg_rman_recovery.conf,覆盖了你前面的设置。

# PostgreSQL 的配置加载顺序是:
postgresql.conf → include 的文件 → postgresql.auto.conf。所以 pg_rman_recovery.conf 里的 restore_command 最终生效了。
7、修复步骤:
bash 复制代码
cd /var/lib/pgsql/15/data

# 备份 postgresql.conf
cp postgresql.conf postgresql.conf.bak

# 注释掉 include 行
sed -i 's/^include/#include/g' postgresql.conf

# 或者直接编辑
vi postgresql.conf
# 找到下面这行,在前面加 # 注释掉
# include = 'pg_rman_recovery.conf' # added by pg_rman 1.3.15

# 重命名或删除 pg_rman_recovery.conf(可选)
## 重命名,让 PostgreSQL 找不到它
mv pg_rman_recovery.conf pg_rman_recovery.conf.disabled

# 重置 postgresql.auto.conf
cat > postgresql.auto.conf << 'EOF'
primary_conninfo = 'user=rep password=123456 host=192.168.6.9 port=5432 sslmode=prefer'
recovery_target_timeline = 'latest'
EOF

# 删除所有 .history 文件
cd /var/lib/pgsql/15/data
find . -name "*.history" -type f -delete
rm -f pg_wal/*.history

# 清空 archive 目录
rm -rf /var/lib/pgsql/15/archive/*

# 启动从库
systemctl start postgresql-15

# 查看日志,验证修复是否成功
tail -100 /var/lib/pgsql/15/data/log/*.log
# 启动成功后,应该看到类似这样的日志:
LOG:  database system was shut down at ...
LOG:  entering standby mode
LOG:  started streaming WAL from primary at ...
LOG:  consistent recovery state reached at ...
补充:关于 pg_rman
复制代码
pg_rman 是一个 PostgreSQL 备份工具,它生成的 pg_rman_recovery.conf 文件通常用于从归档恢复。但在流复制从库场景下,不需要这个配置,因为从库通过流复制直接从主库接收 WAL。
如果以后要用 pg_rman 做备份恢复,记得恢复完成后要把 include 行重新打开。
8、验证主从复制:
1. 检查从库:
bash 复制代码
# 查看从库日志
tail -50 /var/lib/pgsql/15/data/log/*.log

# 检查是否处于恢复模式
psql -U postgres -c "SELECT pg_is_in_recovery();"
# 应返回 t

# 查看流复制状态
psql -U postgres -c "SELECT * FROM pg_stat_wal_receiver;"

# 在主库查看复制连接
psql -U postgres -c "SELECT application_name, state, sync_state, write_lag FROM pg_stat_replication;"
2. 主库最终确认
bash 复制代码
# 主库健康检查
psql -U postgres -c "\l" | grep db_666
psql -U postgres -c "SELECT version();"

SELECT application_name, client_addr, state, sync_state FROM pg_stat_replication;
 application_name | client_addr  |   state   | sync_state 
------------------+--------------+-----------+------------
 walreceiver      | 192.168.x.x | streaming | async


# 检查磁盘使用情况,确保没有不必要的副本
du -sh /var/lib/pgsql/15/data/pg_tblspc/24610/
du -sh /var/lib/pgsql/15/pg_tps/
du -sh /var/lib/pgsql/15/data/pg_tps/ 2>/dev/null  # 如果存在可以删除
相关推荐
爱敲代码的小鱼1 小时前
springsecurity:
java·开发语言·数据库
秋田君1 小时前
QT_QT信号与槽机制
开发语言·数据库·qt
DFT计算杂谈2 小时前
vasp如何计算金属的铁电极化强度?
数据库
EterNity_TiMe_2 小时前
别让告警变成噪音:用Prometheus和Alertmanager搭一套可远程查看的监控系统
数据库·人工智能·ai·postgresql·prometheus·cpolar
不超限3 小时前
Postgresql 数据库 自增字段,Vastbase 无效的问题
数据库
阿坤带你走近大数据3 小时前
Oracle新老SQL写法对比
数据库·sql·oracle
努力努力再努力wz4 小时前
【高性能网络库与HTTP Server系列】:基于主从 Reactor 模型实现高性能 C++ 网络库与 HTTP Server
开发语言·网络·数据结构·数据库·c++·网络协议·http
HackTwoHub4 小时前
某单位 CMS 完整渗透链、SQL 注入→后台拿权→绕过宝塔 Waf 上线 shell
数据库·人工智能·sql·安全·网络安全·系统安全·安全架构
Elastic 中国社区官方博客4 小时前
使用 Elasticsearch 作为 Grafana 的直接替代 Prometheus 后端
大数据·数据库·sql·elasticsearch·搜索引擎·grafana·prometheus