☰
PostgreSQL主从流复制与pgpool-II高可用部署实战
2026/10/27 2:51:22 网站建设 项目流程

简介:PostgreSQL 主从流复制与 pgpool-II 高可用部署文档,面向数据库运维工程师与 DBA,解决主备数据实时同步、读写分离与主库故障自动切换的问题。内容从方案综述入手,讲清主库 WAL 日志经 walsender 推送至备库 walreceiver 落盘、再由 startup 进程回放的同步链路,同时对比同步复制与异步复制在数据一致性和写性能上的取舍,并说明 pgpool-II 连接池、复制、负载均衡与连接数限制各承担什么角色,另附 pg_rman 备份定位。落地部分按实践环境、客户环境准备、资源调整、配置步骤逐章推进,涵盖软件版本选型、postgres 用户与权限、安装目录设置,以及关闭防火墙与 selinux、网络与 hosts 配置、主备时钟同步、sysctl 内核参数与 limit 资源分配等细节,可对照搭建测试环境并完成切换演练。资源包为单个 docx 文件,约 773KB,章节编排清晰便于按需查阅,已有 470 人学习。

1. 两台机器、一个 VIP:这套主从流复制方案先解决什么问题

单机跑 PostgreSQL 的业务,最怕的不是慢,是那台机器没了。磁盘一挂,恢复窗口按小时算;读请求一多,CPU 先趴下。这套 PostgreSQL 主从流复制加 pgpool-II 高可用方案,走的是最朴素也最稳的一条路:用数据库自带的 WAL 流复制做一主一备实时同步,用 pgpool-II 挡在数据库前面做连接池、读写分离和故障切换,再加一个 VIP 让应用端不用改连接串,最后用 pg_rman 做归档备份兜住误删误改。整套东西不依赖云托管、不依赖额外的复制插件,两台数据库机器、一到两台中间件机器就能搭起来。

它适合谁?负责 RHEL/CentOS 上数据库交付、机房不能出网、需要离线装包、还要给甲方交一份能跑通的验收记录的人。方案文档从装包、配流复制、配 pgpool、做 failover 测试一直到备份脚本,是一条完整链路,不是零散的知识点。下面按我自己复现的顺序拆。

2. 原理先立住:WAL 怎么走到备库,pgpool 又卡在哪一层

动手之前把这条链路在脑子里跑一遍,后面配参数时才知道每一行在改什么。这一段不讲教科书定义,只讲你配置时会碰到的那几个开关分别作用在链路的哪一环。

2.1 WAL 从 walwriter 到 startup 进程的四段链路

PostgreSQL 的所有变更先写 WAL,再写数据文件。主库上,后端进程提交事务时把 WAL 记录塞进 WAL buffer,walwriter 周期性把 buffer 刷到pg_wal目录(10 之前叫pg_xlog)落盘。配了备库之后,多出一个 walsender 进程,它盯着 WAL 文件的新内容,通过 TCP 把 WAL 记录推给备库的 walreceiver。walreceiver 收到后先写进备库本地的 WAL 文件,再由 startup 进程持续重放,把备库的数据文件推到和主库一致的状态。

四段链路分别是:主库 WAL buffer → 主库 WAL 文件 → 网络传输 → 备库 WAL 文件 → 备库重放。注意第三段和第四段是分开的,WAL 到了备库磁盘,不等于已经重放完。所以查延迟要看两个 LSN:接收位点和重放位点。很多“备库数据怎么是旧的”问题,根子就在这两个值差得远。

这也解释了一个设计约束:备库是只读的,重放进程和用户查询进程并存,备库上的长查询会阻塞 WAL 重放(max_standby_streaming_delay控制等多久),延迟就会越堆越大。这一点在后面配 pgpool 的读写分离时会被放大。

2.2 同步复制还是异步复制:synchronous_commit 的一个取舍

流复制默认异步。主库提交事务、WAL 写进本地磁盘就返回成功,不等备库确认。好处是写性能不受备库影响,坏处是主库突然断电时,还没传到备库的那一小段 WAL 就丢了。

同步复制要动两个参数:synchronous_standby_names必须非空,synchronous_commit必须是on(更严格的是remote_write或remote_apply)。配好之后,主库提交事务要等指定的备库确认收到 WAL 才返回。代价很直接:网络抖一下,写延迟就上去;备库挂了,主库的写会挂住。

实际交付里我一般这么选:核心交易类的库用同步,把synchronous_standby_names写成ANY 1 (pgs1, pgs2),只要任意一台备库确认就行,避免单台备库故障把主库写死;报表、日志、可容忍少量丢失的库直接异步,synchronous_standby_names留空。

提示:从异步切同步不是改个参数就完事。先把备库延迟压到接近 0,再改synchronous_standby_names,改完立刻观察pg_stat_replication里的sync_state,从async变成sync才算生效。备库一旦跟不上,主库写会卡住,这个切换要在低峰做。

2.3 pgpool-II 四个能力里,真正改变架构的是哪两个

pgpool-II 官方列了四件事:连接池、复制、负载均衡/读写分离、连接数限制。听起来都很有用,但落地时真正决定架构的只有两个。

第一个是负载均衡/读写分离。因为有了流复制,备库的数据和主库一致,SELECT就可以分发到备库去,主库只扛写。这是拿备库换读性能,也是这套方案最直接的收益。

第二个是 watchdog 加 VIP。pgpool 本身如果只有一台,它自己就是新的单点故障。watchdog 让两台 pgpool 互相监控,主的那台持有 VIP,出问题时 VIP 漂到另一台,应用端的连接串不用改。这一步不做,整套高可用的成色要打对折。

至于 pgpool 自带的“复制”功能(native replication),和流复制是两套互斥的东西。流复制是数据库层做复制,pgpool 只负责分发;pgpool 的复制模式是中间件把每条写语句发给所有后端。同一套环境里不要两个都开,配置上表现为backend_clustering_mode选streaming_replication之后,就不要再碰复制相关的开关。这是最容易配串的地方。

连接池和连接数限制属于锦上添花。连接池确实能省掉大量短连接的开销,但要注意它和 PgBouncer 不同,pgpool 的连接池是会话级的,配num_init_children和max_pool时得算清楚:最大并发连接数约等于num_init_children × max_pool,超过就排队。这两个值配小了,应用侧表现为“连得上但很慢”,排查起来很费劲。

2.4 版本分叉:10.3 + pgpool 3.6 与 12.2 + pgpool 4.1 差在哪

方案文档里给了两套环境:旧版 PostgreSQL 10.3 配 pgpool-II 3.6.14,新版 PostgreSQL 12.2 配 pgpool-II 4.1.1,操作系统是 CentOS 6.5 和 7.5。这个分叉不是凑数,两处关键差异会直接决定你抄哪一段配置。

第一处是备库的标识方式。PostgreSQL 12 开始,recovery.conf这个文件彻底不用了,备库标识改成数据目录下的standby.signal空文件,原来写在recovery.conf里的primary_conninfo、restore_command全部挪进postgresql.conf。10.3 上还是老办法,recovery.conf放数据目录,权限 0600。搞反的后果很严重:在 12 上放recovery.conf,数据库会忽略它并当成主库启动,两个主库各自接受写入,数据直接分叉。

第二处是 pgpool 的集群模式参数。3.x 里靠master_slave_mode = on加replication_mode = off来表达“我后面是流复制的一主一备”;4.x 引入了统一的backend_clustering_mode,直接写streaming_replication。3.x 的配置项硬套到 4.x 上,pgpool 启动时会报未知参数或者行为不符预期。

另外 PostgreSQL 10 之后几个函数改了名:pg_current_xlog_location()变成pg_current_wal_lsn(),pg_last_xlog_receive_location()变成pg_last_wal_receive_lsn()。在 12 上执行旧函数名会直接报不存在,写监控脚本时注意版本分支。

3. 系统层准备与单机安装:把两台机器的地基打平

这一段是纯体力活,但漏一项后面就要返工。我的习惯是先在两台机器上把系统层的东西全部对齐,再开始装数据库,出问题时能少一个变量。

3.1 离线环境的依赖包:先缓存再打包

客户机房不能出网是常态,方案文档里专门给了“RHEL 安装环境获取脚本”,思路就是在一台能联网的同版本机器上把依赖包缓存下来,打包带进去。我的做法更直接一点:用yum --downloadonly把编译和运行依赖一次性拉齐,再用createrepo做成一个本地源。

# 在能联网的同版本机器上执行,把依赖包缓存到指定目录 mkdir -p /opt/rpms yum install -y --downloadonly --downloaddir=/opt/rpms \ gcc make readline-devel zlib-devel openssl-devel \ pam-devel libxml2-devel libxslt-devel tcl-devel \ flex bison perl-ExtUtils-Embed # 生成本地源元数据,压缩后带到离线机器 createrepo /opt/rpms tar czf rpms.tar.gz -C /opt rpms

带进机房后解压,写一个指向file:///opt/rpms的 repo 文件,yum clean all && yum makecache,之后照常yum install。这里的参数说明:--downloadonly只下载不安装,--downloaddir指定落盘位置,createrepo生成repodata目录,缺了它 yum 认不出这是个源。

一个血泪经验:一定要在同一大版本的机器上缓存。CentOS 6.5 和 7.5 的 glibc 版本不同,包混用会出现依赖无法满足的死循环,最后只能一台一台手工rpm -ivh --nodeps,非常难受。

3.2 账号、目录、权限:postgres 用户与安装路径

两台机器上postgres用户的 UID/GID 建议保持一致,后面做目录挂载和数据同步时省事。

groupadd -g 1100 postgres useradd -g postgres -u 1100 -m -d /home/postgres -s /bin/bash postgres echo 'Pg_Install#2024' | passwd --stdin postgres # 规划四个目录,职责分开 mkdir -p /opt/pgsql/app # 安装目录,PGHOME mkdir -p /opt/pgsql/data # 数据目录,PGDATA mkdir -p /opt/pgsql/archive # WAL 归档目录 mkdir -p /opt/pgsql/backup # pg_rman 备份根目录 chown -R postgres:postgres /opt/pgsql chmod 700 /opt/pgsql/data # 数据目录必须是 0700,否则 initdb 直接拒绝

参数说明:-d指定家目录,-m自动创建;chmod 700是 PostgreSQL 的硬性要求,权限放宽到 750 以上initdb会报错退出。安装目录和数据目录分开,好处是升级二进制时数据目录完全不动,回滚只需要切回旧目录。

3.3 防火墙、SELinux、hosts、时钟、sysctl、limit 六项

这六项里,前三项决定两台机器能不能正常通信,后三项决定数据库跑起来之后会不会突然出怪问题。

防火墙和 SELinux 在实验环境直接关掉,生产环境按需放行 5432、9999、9000、9898 这几个端口。SELinux 强制模式下,pg_basebackup拷过来的数据目录会带上错误的上下文,startup 进程读不了文件,日志里一堆 permission denied,看着像权限问题其实是 SELinux 拦的。临时排查用getenforce确认,设置SELinux=permissive之后抓audit.log生成策略,别长期关着。

hosts 文件两台机器写成一样,主机名和 IP 对应清楚,后面 pgpool 的backend_hostname、watchdog 的hostname和wd_hostname都要能解析。时钟同步用 chrony,两台机器偏差超过几秒时,pgpool 的 health check 和日志时间线会错乱,排查故障时非常误导人。

系统参数这块,PostgreSQL 主要吃共享内存和信号量:

# /etc/sysctl.d/99-postgres.conf kernel.shmmax = 68719476736 # 单块共享内存上限,字节,这里 64GB kernel.shmall = 4294967296 # 共享内存总页数,按 4K 页算 kernel.sem = 50100 64128000 50100 1280 vm.swappiness = 1 # 尽量不用 swap vm.overcommit_memory = 2 # 禁止过度分配,配合 overcommit_ratio vm.overcommit_ratio = 90

参数说明:shmmax要大于等于你打算给的shared_buffers;shmall单位是页,shmmax除以 4096 就是最少需要的值;overcommit_memory = 2时系统按swap + 物理内存 × ratio / 100来卡申请,配得太紧会导致数据库启动时报could not resize shared memory segment。改完sysctl -p生效。

资源限制放在/etc/security/limits.d/99-postgres.conf:

postgres soft nofile 65535 postgres hard nofile 65535 postgres soft nproc 16384 postgres hard nproc 16384 postgres soft memlock unlimited postgres hard memlock unlimited

nofile不够的表现是连接数一多就报too many open files,而且是在业务高峰期才出现,很有欺骗性。memlock设成 unlimited 是为后面用大页做准备,不用大页也可以先放开。

3.4 initdb 与主库参数:wal_level、max_wal_senders、wal_keep_segments

装完二进制之后初始化主库。源码安装的话./configure --prefix=/opt/pgsql/app之后make && make install,方案文档的附件脚本就是这条路。

su - postgres /opt/pgsql/app/bin/initdb -D /opt/pgsql/data \ -E UTF8 --locale=C \ --auth-local=peer --auth-host=md5

参数说明:-E UTF8指定编码,--locale=C避免排序规则带来的性能问题,--auth-local=peer让本机 socket 连接靠系统用户认证,--auth-host=md5让远程 TCP 连接走密码。生产上如果是 14 以后的版本可以换scram-sha-256,但要注意 pgpool 版本是否支持,方案里给的 12.2 配 pgpool 4.1.1 用 md5 更稳。

主库的postgresql.conf要改的核心就几行:

listen_addresses = '*' port = 5432 # 流复制三件套 wal_level = replica # 10 之前叫 hot_standby,10 起用 replica max_wal_senders = 10 # 要大于备库数量 + 备份进程数 wal_keep_segments = 128 # 12 及以前用这个,13 起改成 wal_keep_size = 2GB # 备库可读 hot_standby = on hot_standby_feedback = on # 减少备库长查询被取消的概率 # 同步策略:先异步,稳定后再评估 synchronous_commit = on synchronous_standby_names = '' # 归档,pg_rman 依赖它 archive_mode = on archive_command = 'test ! -f /opt/pgsql/archive/%f && cp %p /opt/pgsql/archive/%f'

参数说明里最容易踩的是wal_keep_segments和wal_keep_size的版本差异。12 上写wal_keep_size会报未知参数,数据库起不来;反过来 13 上写wal_keep_segments一样报错。这个值的作用是主库保留多少 WAL 不删,备库断线重连后靠它补齐,配小了备库追不上就得重做基础备份。

archive_command那条命令里的test ! -f是防覆盖,同名文件已存在就返回非零,归档进程会重试。归档目录写满是很常见的事故,建议单独挂盘并加监控。

3.5 复制用户与 pg_hba.conf 放行

复制用户单独建,不要用超级用户:

CREATE ROLE repl WITH REPLICATION LOGIN PASSWORD 'Repl_2024#x';

pg_hba.conf里必须显式放行replication这个伪库,这是新手最容易漏的一行:

# TYPE DATABASE USER ADDRESS METHOD local all all peer host all all 10.0.0.0/24 md5 host replication repl 10.0.0.0/24 md5

replication不是真实的数据库名,是连接请求类型。只写host all all的话,pg_basebackup连上来会报no pg_hba.conf entry for host ... database replication,日志里看得清清楚楚,但很多人第一反应是去查密码。

改完pg_ctl reload,然后本机验证一次:

psql -h 10.0.0.11 -U repl -d postgres -c "IDENTIFY_SYSTEM;" -w # 复制协议专用,正常会返回 systemid、timeline、xlogpos

3.6 pg_basebackup 拉备库与 standby.signal

主库跑起来之后,备库上把整个数据目录从主库拉过来:

# 备库执行,先确保 /opt/pgsql/data 为空 rm -rf /opt/pgsql/data/* /opt/pgsql/app/bin/pg_basebackup \ -h 10.0.0.11 -p 5432 -U repl \ -D /opt/pgsql/data \ -Fp -Xs -P -R -v

参数说明:-Fp用普通文件格式而不是 tar 包,方便直接看目录;-Xs让 WAL 通过流复制通道一起传,避免还要单独配restore_command;-P显示进度,几百 G 的库没有进度条很难判断是不是卡住了;-R是关键,它会自动在数据目录下生成standby.signal并把primary_conninfo写进postgresql.conf(12 及以上),10 及以前生成的是recovery.conf。

如果是 12 以上又没用-R,需要手工补:

touch /opt/pgsql/data/standby.signal cat >> /opt/pgsql/data/postgresql.conf <<'EOF' primary_conninfo = 'host=10.0.0.11 port=5432 user=repl password=Repl_2024#x application_name=pgs1' recovery_target_timeline = 'latest' EOF chmod 600 /opt/pgsql/data/postgresql.conf

application_name要写,主库的pg_stat_replication里靠它区分是哪台备库,配同步复制时synchronous_standby_names里填的也是这个名字。recovery_target_timeline = 'latest'让备库在主库发生过时间线切换后还能跟上去。

备库起来之后,主库上查:

SELECT application_name, client_addr, state, sync_state, sent_lsn, write_lsn, flush_lsn, replay_lsn, pg_wal_lsn_diff(sent_lsn, replay_lsn) AS lag_bytes FROM pg_stat_replication;

state是streaming、lag_bytes在几百 KB 以内,就算正常。备库上确认自己处于恢复状态:

SELECT pg_is_in_recovery(), pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn();

pg_is_in_recovery()返回t才对。返回f说明备库被当成主库启动了,立刻停掉查原因,这种状态下如果应用连上来写过数据,就只能重做备库。

4. pgpool-II 配置与故障切换:从 pgpool.conf 到 VIP 漂移

pgpool 这一层是把前面搭好的主备变成“应用无感”的关键。配置项多,但真正决定行为的就那么几组:后端节点定义、健康检查、watchdog 与 VIP、鉴权链。

4.1 backend_clustering_mode 与 backend 列表

4.x 的第一组配置:

backend_clustering_mode = 'streaming_replication' listen_addresses = '*' port = 9999 pid_file_name = '/opt/pgpool/run/pgpool.pid' logdir = '/opt/pgpool/log' backend_hostname0 = '10.0.0.11' backend_port0 = 5432 backend_weight0 = 1 backend_flag0 = 'ALWAYS_PRIMARY|DISALLOW_TO_FAILOVER' backend_hostname1 = '10.0.0.12' backend_port1 = 5432 backend_weight1 = 1 backend_flag1 = 'DISALLOW_TO_FAILOVER'

backend_weight是读请求的分发权重,主备权重都给 1 的话,读请求会一半打到主库。如果想让读全走备库,把backend_weight0设成 0。不过要注意,权重为 0 的节点在某些版本里仍然会接受写,只是不接读。

backend_flag这两个值的含义值得说清楚:ALWAYS_PRIMARY告诉 pgpool 0 号节点永远是主库,DISALLOW_TO_FAILOVER表示这个节点出问题不要把它从后端列表里摘掉。在流复制模式下我一律这么配,原因是 pgpool 自己不做 promote,主库挂了由外部脚本或者人工把备库提起来,pgpool 只需要知道“谁是主库”就够了。让 pgpool 自动摘节点,反而会出现两个节点都被摘掉、整个集群不可用的情况。

3.x 上对应的写法是:

master_slave_mode = on master_slave_sub_mode = 'streaming' replication_mode = off

4.2 health_check 与 failover_command:谁来判断、谁来执行

健康检查是 pgpool 判断后端死活的唯一依据,配错了会出现“数据库明明活着,pgpool 说它挂了”。

health_check_period = 10 health_check_timeout = 20 health_check_user = 'pgpool_check' health_check_password = 'Pool_2024#x' health_check_database = 'postgres' health_check_max_retries = 3 health_check_retry_delay = 5 failover_command = '/opt/pgpool/bin/failover.sh %d %H %R %m %M %P %t %D'

health_check_user必须是一个真实存在、能连postgres库的普通用户,且在后端的pg_hba.conf里放行了 pgpool 所在机器的 IP。health_check_period = 10配合max_retries = 3,意味着最快也要 30 秒才能判定节点死亡,这个窗口内应用会看到连接错误。

failover_command里的占位符含义要背下来:%d是失败的节点 ID,%H是该节点主机名,%D是数据目录,%R是数据库目录,%m是旧主库节点 ID,%M是新主库节点 ID,%P是旧主库 ID,%t是时间戳。脚本里最常见的写法是判断%m和%d相等,说明挂的是主库,于是去备库上pg_ctl promote,然后touch一个触发文件让 pgpool 重读配置。

注意:failover_command是以运行 pgpool 的操作系统用户身份执行的,脚本必须对该用户可执行,且里面用到的ssh、pg_ctl要用绝对路径。PATH 在守护进程环境下和登录 shell 不一样,这是排查 “command not found” 的第一现场。

4.3 watchdog 与 delegate_IP:VIP 漂移的两个前提

单台 pgpool 是新的单点。要让它高可用,两台 pgpool 组成 watchdog 集群,共享一个 VIP:

use_watchdog = on delegate_IP = '10.0.0.100' hostname0 = 'pgpool01' wd_port0 = 9000 pgpool_port0 = 9999 hostname1 = 'pgpool02' wd_port1 = 9000 pgpool_port1 = 9999 # 两台机器必须完全一致 wd_hostname = 'pgpool01' heartbeat_destination0 = '10.0.0.21' heartbeat_destination_port0 = 9000 heartbeat_device0 = 'eth0' if_up_cmd = 'sudo /sbin/ip addr add $_IP_$/24 dev eth0 label eth0:0' if_down_cmd = 'sudo /sbin/ip addr del $_IP_$/24 dev eth0' arping_cmd = 'sudo /usr/sbin/arping -U $_IP_$ -w 1 -I eth0'

两个前提必须满足,缺一个 VIP 就不漂:

第一,wd_hostname必须是本机hostname命令的真实输出,而且两台机器的 hosts 里都能解析到对方。写错了 watchdog 起不来,日志里报failed to bind.

第二,sudo免密必须配好,visudo里给 pgpool 的运行用户加上/sbin/ip和/usr/sbin/arping的 NOPASSWD。$_IP_$是 pgpool 的占位符,命令串本身在配置文件里,不要在外面再套一层 shell 变量,否则会被提前展开成空字符串。

arping那一条是刷新交换机 ARP 缓存的,不加的话 VIP 漂过去了但网关还指着老机器,表现是“主机上能看到 VIP,应用连不上”。这个坑非常典型。

4.4 pool_hba 与 pool_passwd:客户端连的是 pgpool,不是 PG

应用端连的是 9999 端口,认证发生在 pgpool 这一层,所以有两份鉴权配置要管。

pool_hba.conf的格式和 PostgreSQL 的pg_hba.conf一样,但方法只能写md5、trust、reject:

host all appuser 10.0.0.0/24 md5 host all all 10.0.0.0/24 md5

pool_passwd里存的是同一个用户的密码,pgpool 拿它去连后端。生成方式是:

# 3.x 用 pg_md5 pg_md5 -m -f /opt/pgpool/etc/pool_passwd -u appuser # 4.x 用 pg_enc pg_enc -m -f /opt/pgpool/etc/pool_passwd -u appuser

执行后会提示输入密码,生成一行appuser:md5xxxx。这里有个必须记住的前提:后端 PostgreSQL 的认证方式如果是scram-sha-256,pgpool 4.x 虽然支持,但pool_passwd里存的格式和 md5 不一样,混用会出现“pgpool 能连上,但应用报密码错误”。方案里给的 12.2 环境用 md5,能避开这一类问题。

还有一个隐蔽的坑:pool_passwd文件权限要 600,且属主是 pgpool 的运行用户。权限不对 pgpool 启动时会警告,但不会报错退出,表现是认证一直失败。

4.5 读写分离的两个开关与延迟反噬

load_balance_mode = on statement_level_load_balance = off sr_check_period = 10 sr_check_user = 'pgpool_check' sr_check_password = 'Pool_2024#x' delay_threshold = 10MB

load_balance_mode = on打开读写分离,statement_level_load_balance = off表示以会话为单位选节点,而不是每条语句都重新选。后者打开之后,同一个事务里的SELECT可能跑到备库,而它前面刚写过数据,读到的就是旧值。

delay_threshold是延迟阈值,备库落后超过这个值就不再往它发读请求。设成0等于关闭延迟检查,这也是默认值,很多人不知道。在备库延迟大的时候,读请求照样打过去,业务上表现为“刚提交的数据查不到”,而且时有时无,非常像玄学问题。

真要临时强制某条语句走主库,SQL 注释就行:

/*NO LOAD BALANCE*/ SELECT * FROM orders WHERE id = 10086;

这个注释会跟着语句一起发给 pgpool,pgpool 解析到就直接转发主库。适合写完之后立刻要读的场景。

4.6 failover 测试:停进程、停 pgpool、看 VIP

配置完不测等于没配。方案文档里的第十章给了两个测试,我补上第三个。

# 测试一:停主库进程,验证 pgpool 是否把备库提起来 su - postgres -c "/opt/pgsql/app/bin/pg_ctl -D /opt/pgsql/data -m fast stop" # 观察池内节点状态(在 pgpool 机器上执行) psql -h 10.0.0.100 -p 9999 -U pgpool -d postgres -c "SHOW pool_nodes;" # 测试二:停持有 VIP 的那台 pgpool,验证 VIP 是否漂移 pgpool -m fast stop ip addr show eth0 | grep 10.0.0.100 # 测试三:停备库,验证主库写是否受影响(同步复制下会挂住,异步复制下无感)

SHOW pool_nodes的输出重点看三列:status(up/down)、role(primary/standby)、replication_delay。切完之后 0 号节点应该变成 standby,1 号变成 primary。如果两个都是 down,回到 4.1 检查backend_flag。

测试一做完要手工把备库降回 standby、把老主库补数据重做,别在测试环境留一个分裂的主备。

5. 排查避坑:五条翻车记录,每条都对应一个日志特征

下面这五条是我在复现这套方案时真实卡过的,按“现象 → 原因 → 解决”写,日志关键字直接给出来,方便对着搜。

5.1 现象:pg_basebackup 跑到一半卡住,或者报 could not connect

原因:三种可能,按概率排——pg_hba.conf没放行replication类型的连接;主库的listen_addresses还是localhost;备库的/opt/pgsql/data目录非空。12 之后的pg_basebackup要求目标目录为空或不存在,目录里有东西时会先删再写,但中途失败会留下半截文件,第二次跑又报错。

解决:先在备库上用psql -h <主库IP> -U repl -d postgres测连接,能连上再跑备份;备份前rm -rf /opt/pgsql/data/*并确认目录权限是postgres:postgres 700。如果是防火墙挡了,telnet <主库IP> 5432会直接卡住,比看日志快。

5.2 现象:备库起来了,但pg_is_in_recovery()返回f

原因:standby.signal没生成。12 以上版本靠这个空文件判断“我是备库”,文件不在,数据库就按主库启动。用-R参数会自建,手工搭的时候经常忘。10 及以前则是recovery.conf放错位置或者权限不是 600。

解决:停库,touch /opt/pgsql/data/standby.signal,确认postgresql.conf里有primary_conninfo,再起库。起来之后立刻查pg_is_in_recovery(),t才算过。如果这台备库已经接过写,别犹豫,删数据目录重做基础备份。

5.3 现象:pgpool 日志报all backend nodes are down,但数据库明明是活的

原因:health_check_user连不上后端。可能是用户不存在、密码错、后端pg_hba.conf没放行 pgpool 的 IP,或者 pgpool 用了pool_passwd里的密码而health_check_password写的是明文——两个来源不一致时以明文为准,但很多人改了一处忘了另一处。

解决:手工用同样的用户名密码从 pgpool 机器上psql -h 10.0.0.11 -U pgpool_check -d postgres,能连上说明是 pgpool 侧配置问题;连不上说明是后端pg_hba或密码问题。另外把log_min_messages调到debug1,日志里会打印每条 health check 的具体失败原因,比猜快得多。

5.4 现象:停掉主 pgpool 之后,VIP 没有漂移,或者漂过去了应用连不上

原因:没漂移,多半是wd_hostname和实际 hostname 不一致,或者两台 pgpool 的心跳端口 9000 被防火墙挡了,watchdog 认为对方还活着。漂过去连不上,几乎都是arping_cmd没配或者执行失败,交换机的 ARP 缓存还指向老机器。

解决:show pool_nodes之前先看pgpool -n前台启动的日志,watchdog 成员的上下线状态会打得很清楚。arping那条命令手工执行一次,看是否报Operation not permitted,是的话补 sudo 权限。VIP 漂移这种事,配完必须实测一遍,不能靠“应该没问题”。

5.5 现象:读写分离开着,刚写入的数据查不到

原因:delay_threshold = 0关掉了延迟检查,备库落后多少都会收到读请求;或者应用在同一个连接里“先写后读”,而statement_level_load_balance = on把后面的SELECT发到了备库。

解决:把delay_threshold设成一个业务能接受的值(比如 10MB 或 1s 对应的字节数),让 pgpool 自动跳过延迟大的备库;对写后立即读的语句加/*NO LOAD BALANCE*/注释。根本上还是要看备库为什么延迟,pg_stat_replication里的replay_lsn和sent_lsn差距持续扩大,通常是备库上有长查询在阻塞重放。

6. 备份兜底与验收:pg_rman 是这套架构的后悔药

流复制解决的是“机器挂了”,解决不了“数据被误删”。DELETE不带WHERE的语句会原封不动复制到备库,几分钟内两台机器一起完蛋。所以最后一块必须落在归档备份上。

方案里给的 pg_rman 思路是物理备份加 WAL 归档,能恢复到任意时间点。装的时候有个硬约束:pg_rman 的版本必须和 PostgreSQL 的大版本对应,12 的库要用支持 12 的 pg_rman,版本错配会在pg_rman init阶段就报错。初始化完之后,日常三条命令就够:

<p> <a href="https://download.csdn.net/download/Jay_Fred/88477949" style="color:#ec7500;font-size:14px;"> 本文还有配套的精品资源,点击获取 </a> <img alt="menu-r.4af5f7ec.gif" src="https://csdnimg.cn/release/wenkucmsfe/public/img/menu-r.4af5f7ec.gif" style="width:16px;margin-left:4px;vertical-align:text-bottom;cursor:text;"> </p>

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

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

立即咨询