☰
PostgreSQL流复制+pgpool-II高可用架构搭建与故障转移实战
2026/10/14 17:21:53 网站建设 项目流程

简介:Postgres主从流复制与pgpool高可用部署方案文档,面向需要搭建数据库高可用架构的DBA、运维工程师与架构师,重点解决主备库数据实时同步、故障自动切换和读写分离问题。文档共1个docx文件,压缩包大小约773KB,内容围绕流复制原理、同步与异步模式选择、pgpool-II连接池与负载均衡机制展开,并给出从环境准备到配置启动的完整实施路径。包含软件选型、用户权限、防火墙与selinux调整、hosts与时钟同步、sysctl和limit资源分配等实践要点,主库配置、备库初始化、pgpool切换策略及启动测试均有说明,适合作为生产或测试环境搭建时的操作参考。已有469人学习,对正在规划PostgreSQL高可用方案的团队具有直接借鉴价值。

1. 为什么这套方案值得搭:一条文档把主从、读写分离和备份都串起来

PostgreSQL 流复制加 pgpool-II 这套组合,在数据库高可用场景里几乎是标配答案。核心并不复杂:主库把 WAL 日志通过流复制协议实时传给备库,备库不停重放这些日志保持数据同步;pgpool-II 作为中间层,把连接池、读写分离、故障转移和虚拟 IP 漂移全部收拢到一起;pg_rman 负责离线备份兜底。你拿到这份文档,能一步不跳地把这三层全部搭起来,落地到 RHEL 或 CentOS 环境里,中间还包含旧版本(10.3)和新版本(12.2)的配置差异对照。适合正在做数据库运维、需要搭建高可用环境、或者被领导要求“三天内把主从架构搞出来”的从业者,新手按步骤能跑通,熟手可以直接抄参数。

2. 先把系统地基打牢:账号、防火墙、内核参数与离线部署四件事

流复制和 pgpool 能不能稳定跑起来,一半的命根子在系统层配置。很多人一上来就装 PostgreSQL,结果备库连不上、连接数暴涨、虚拟 IP 漂移失败,最后排查半天发现全是系统层的问题。这一章把客户环境准备阶段四件必做的事讲透。

2.1 postgres 用户、安装目录与权限

所有节点上都要创建 postgres 系统用户,用于运行数据库实例。密码按你们公司的密码策略设就好,但两端保持一致,后面很多管理操作要基于这个用户做 SSH 免密。安装目录我习惯统一放到 /opt/postgres,备库和主库保持一致,避免日后路径混乱。

useradd postgres echo "postgres:YourPassword" | chpasswd mkdir -p /opt/postgres chown -R postgres:postgres /opt/postgres

这里有个容易忽略的点:不仅数据目录要归 postgres 用户,软件安装目录、归档日志目录、备份目录、pgpool 的运行时目录都得让 postgres 有读写权限,否则后续做归档、备份、failover 脚本执行时会遇到权限拒绝。目录权限建议直接 0755 起步,数据目录和数据文件用 0700,别在这上面省事。

2.2 防火墙、SELinux 与网络配置

主备节点之间要走 5432 端口的流复制,pgpool 对外提供 9999,pcp 管理端口 9898,watchdog 通信用 9000。防火墙这三条要放通,不然备库的 walreceiver 永远连不上主库。

firewall-cmd --permanent --add-port=5432/tcp firewall-cmd --permanent --add-port=9999/tcp firewall-cmd --permanent --add-port=9898/tcp firewall-cmd --permanent --add-port=9000/tcp firewall-cmd --complete-reload

SELinux 建议直接关掉,这是无数人踩过的坑。PostgreSQL 和 pgpool 都不是默认带 SELinux 策略的软件,开着 enforcing 模式,明明网络通、端口开、权限对,但连不上,日志里还看不到明显报错,纯属玄学问题。

sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config setenforce 0

hosts 文件同样必须配置。主备节点互相通过主机名访问,如果不写 hosts,DNS 解析出问题或者解析到错误地址,流复制会反复断连。

cat >> /etc/hosts <<EOF 192.168.1.10 pg-master 192.168.1.11 pg-standby 192.168.1.100 pgpool-vip EOF

注意虚拟 IP(192.168.1.100)也要写进去,pgpool 的 watchdog 在监测后端节点时要用到。

2.3 时钟同步与 sysctl、limit 参数

主备节点时间不一致,会导致 WAL 时间戳错乱,归档日志的命名和恢复顺序出问题。低版本环境用 ntpdate 定时同步,高版本用 chrony,两条路子都能走,关键是别让两端时钟漂移超过阈值。

sysctl 参数重点调整共享内存和连接数相关项:

cat >> /etc/sysctl.conf <<EOF kernel.shmmax = 17179869184 kernel.shmall = 4194304 net.core.somaxconn = 65535 net.ipv4.tcp_max_syn_backlog = 65535 fs.file-max = 6815744 EOF sysctl -p

shared buffer 如果打算设置到 4GB,shmmax 就要大于 4GB,否则 PostgreSQL 启动时共享内存分配失败。这是最常见的内存参数翻车现场。文件描述符的 limit 要放开,数据库连接多的时候,默认 1024 的 nofile 根本不够用。

cat >> /etc/security/limits.conf <<EOF postgres soft nofile 65536 postgres hard nofile 65536 postgres soft nproc 65536 postgres hard nproc 65536 EOF

2.4 离线环境包准备:一条可迁移到内网的路径

客户环境经常不能联网,所以文档里专门提供了“RHEL 安装环境获取脚本”。思路是在能联网的机器上先执行脚本,把 RPM 包缓存到本地仓库,然后搬到内网机器上离线安装。这套流程在银行、政务类项目里非常实用。

离线安装的核心是本地 yum 源。把缓存好的 RPM 拷到内网机器上,createrepo 生成仓库元数据,再把 repo 文件指向本地目录,之后安装依赖包就和在线环境一样顺畅。需要注意的是 PostgreSQL 和 pgpool 走的是源码编译安装,依赖包走 yum,两种安装方式互不冲突,但编译工具链(gcc、make、readline-devel 等)必须在离线包里备齐。

3. 流复制搭建实战:从安装 PostgreSQL 到主备节点生效

前面系统层弄完,这台机器才能谈得上“适合跑数据库”。接下来进入正题:装 PostgreSQL、初始化实例、配主备流复制。这一章覆盖 10.3 和 12.2 两代版本,重点区分配置上的版本差异。

3.1 编译安装与环境变量

源码包解压后进入目录,configure、make、make install 三步走。10.3 和 12.2 的编译方式没有本质区别,但依赖包版本有要求,12.2 需要更高版本的 gcc 和 readline。

tar -zxvf postgresql-12.2.tar.gz cd postgresql-12.2 ./configure --prefix=/opt/postgres/pg12 --with-pgport=5432 --with-openssl make -j 4 && make install

--with-openssl 建议加上,后面 pgpool 做认证和 SSL 连接时会用到。--with-pgport 指定默认端口,避免后续到处改端口参数。编译时间取决于机器配置,四核机器大约 10 到 15 分钟。

环境变量写进 postgres 用户的 .bash_profile,主备节点一致:

echo 'export PATH=/opt/postgres/pg12/bin:$PATH' >> ~/.bash_profile echo 'export LD_LIBRARY_PATH=/opt/postgres/pg12/lib:$LD_LIBRARY_PATH' >> ~/.bash_profile source ~/.bash_profile

注意 LD_LIBRARY_PATH 必须加。源码编译安装的 PostgreSQL,libpq 等动态库不在系统默认搜索路径里,不加这个变量,pgpool 编译时会找不到 libpq,psql 客户端也可能报找不到共享库文件。

3.2 主库参数:三处必须改的配置

初始化数据库用 initdb,然后改 postgresql.conf。12.2 版本里,流复制相关的核心参数就三个:wal_level、max_wal_senders、hot_standby。

initdb -D /data/pgdata --encoding=UTF8 --locale=en_US.UTF-8
# postgresql.conf (master) wal_level = replica max_wal_senders = 10 wal_keep_size = 1024 hot_standby = on listen_addresses = '*'

10.3 版本里 wal_keep_segments 的单位是“段的个数”(每个段 16MB),而 12.2 里 wal_keep_size 直接用 MB 表示,这是跨版本最容易踩的坑。wal_level 从 9.6 之后就用 replica 替代了 hot_standby 这个取值,10.3 和 12.2 都写 replica,不要再用旧文档里的 hot_standby。

max_wal_senders 决定有多少个备库可以同时连接主库拉取 WAL。两个节点各一个就够,但考虑到将来扩容,设 10 是个稳妥值。hot_standby = on 是让备库能接受只读查询的关键,没有它,备库只能恢复数据,客户端连上去查不了表。

访问控制文件 pg_hba.conf 加两行,一个是应用连接,一个是复制连接:

host all all 192.168.1.0/24 md5 host replication replica_user 192.168.1.0/24 md5

replication 那条很多人会写漏。流复制连接走的是 replication 数据库伪装的连接类型,普通 host all 那行覆盖不到,必须单独开。replica_user 这个用户后面建。

3.3 备库克隆与启动标识

备库不手动 initdb,直接从主库拉一份基础备份。pg_basebackup 是官方推荐的方式,比手动打包数据目录干净得多。

pg_basebackup -h 192.168.1.10 -U replica_user -D /data/pgdata -F p -X stream -R

-F p 表示输出为普通文件格式,不要打成 tar 包。-X stream 表示 WAL 日志跟随流复制实时传送,不单独拉历史归档。-R 参数会在数据目录里自动生成 standby.signal 文件(12.2)或 recovery.conf(10.3)。

12.2 的 standby.signal 只是个空文件,存在即表示该节点以 standby 模式启动。主库连接信息写到 postgresql.conf 里:

# postgresql.conf (standby, 12.2) primary_conninfo = 'host=192.168.1.10 port=5432 user=replica_user password=xxx'

10.3 则需要在 recovery.conf 里配置,文件内容三行:

standby_mode = 'on' primary_conninfo = 'host=192.168.1.10 port=5432 user=replica_user password=xxx' trigger_file = '/tmp/pg_failover_trigger'

trigger_file 参数是 10.3 及以前配置主备自动切换时用的,备库检测到触发文件存在就自动提升为主库。12.2 里这个机制改成了 pg_ctl promote 命令,不再需要 trigger_file。你如果拿着旧教程配置 12.2,去找 recovery.conf 会发现根本找不到,方向就错了。

3.4 启动与验证

先启动主库,再启动备库。备库启动后,日志里会出现类似“database system is ready to accept read only connections”的提示,表示它已经进入恢复模式并对外提供只读服务。

-- 在主库执行,查看复制状态 SELECT pid, usename, application_name, state, sync_state FROM pg_stat_replication;

state 列显示 streaming 表示流复制在建。sync_state 列这里能看到同步模式是否生效:

pid | usename | application_name | state | sync_state 123 | replica_user | walreceiver | streaming | async

sync_state 为 async 表示当前是异步复制模式。如果想改成同步复制,主库加一行配置:

synchronous_standby_names = '*' synchronous_commit = on

注意:同步复制要谨慎启用。它要求主库提交事务时必须等到备库写完 WAL 才返回成功,备库宕机时主库的写事务会全部挂起。生产环境如果无法接受这个风险,保持异步模式更稳妥。

4. pgpool-II 部署与配置:连接池、负载均衡和故障转移的落地细节

流复制搭好之后,数据库层面已经有了一份热备数据。但要让应用无感知地使用这些节点,还需要 pgpool-II 在前面做调度。这一章讲安装和核心配置,重点放在 4.1 版本的参数语义上。

4.1 编译安装与数据库函数注册

pgpool-II 的源码编译和 PostgreSQL 类似。configure 时一定要指对 PostgreSQL 的安装路径,否则找不到 pg_config,扩展函数装不进去。

tar -zxvf pgpool-II-4.1.1.tar.gz cd pgpool-II-4.1.1 ./configure --prefix=/opt/postgres/pgpool --with-pgsql=/opt/postgres/pg12 make -j 4 && make install

安装完成后,还要往 PostgreSQL 里注册几个 pgpool 扩展函数。这些函数是 pgpool 实现后端健康检查、在线恢复等功能必需的,不装的话,pgpool 启动可能正常,但 failover 时调用的恢复流程会报错。

cd /opt/postgres/pgpool-4.1.1/src/sql make install psql -h 192.168.1.10 -U postgres -d postgres -f insert_lock.sql

insert_lock.sql 是核心中的核心。pgpool 的复制模式(replication mode)下,并发插入时保证序列不冲突全靠它。我们的场景走的是 streaming 复制模式,这个函数不是必装,但装了没坏处,后续如果要切换模式就不用再补。

环境变量同样要配:

export PATH=/opt/postgres/pgpool/bin:$PATH export LD_LIBRARY_PATH=/opt/postgres/pgpool/lib:$LD_LIBRARY_PATH

4.2 pgpool.conf 核心参数:连接池、负载均衡与 watchdog

pgpool.conf 是整套配置的枢纽,参数量很大,但真正决定架构行为的就那十几个。以下配置文件来自文档里的 4.X 版本示例:

# pgpool.conf listen_addresses = '*' port = 9999 socket_dir = '/var/run/pgpool' pcp_listen_addresses = '*' pcp_port = 9898 # 后端节点定义 backend_hostname0 = '192.168.1.10' backend_port0 = 5432 backend_weight0 = 1 backend_data_directory0 = '/data/pgdata' backend_flag0 = 'ALLOW_TO_FAILOVER' backend_hostname1 = '192.168.1.11' backend_port1 = 5432 backend_weight1 = 1 backend_data_directory1 = '/data/pgdata' backend_flag1 = 'ALLOW_TO_FAILOVER' # 连接池 connection_cache = on # 负载均衡与读写分离 load_balance_mode = on master_slave_mode = on master_slave_sub_mode = 'stream' # 健康检查 health_check_period = 10 health_check_user = 'pgpool_check' health_check_password = 'check_pass' # 故障转移脚本 failover_command = '/etc/pgpool/failover.sh %d %H %P'

backend_weight 是负载均衡的权重分配。两个节点都是 1,表示读请求按 1:1 轮询分发。如果你的备库性能比主库差,可以把备库权重调低,比如主库 2、备库 1,让读流量向主库倾斜。

master_slave_mode = on 加上 master_slave_sub_mode = 'stream',这是针对 PostgreSQL 流复制场景的标准组合。在这个模式下,pgpool 自动把写操作发给主节点,把读操作按权重分发给所有节点。复制模式(replication mode)是给多主复制用的,不要混用。

健康检查用户专门建一个:

CREATE USER pgpool_check WITH PASSWORD 'check_pass';

这个用户会在 watchdog 节点之间心跳检测、后端节点存活探测时使用。权限不需要太高,CONNECT 权限就够,但 pg_hba.conf 里必须对这个用户放行。

watchdog 部分单独看,它是实现虚拟 IP 漂移的关键:

use_watchdog = on wd_hostname = '192.168.1.10' wd_port = 9000 delegate_ip = '192.168.1.100' wd_escalation_command = '/etc/pgpool/escalation.sh'

两个节点的 pgpool.conf 里,wd_hostname 分别写本机地址,delegate_ip 写同一个虚拟 IP。正常情况下虚拟 IP 在主库所在节点的 pgpool 上,主库宕机触发 failover 后,备库节点的 pgpool 会接管这个 IP。应用层连接的始终是虚拟 IP,主备切换对应用透明。

4.3 认证文件与 failover 脚本

pcp.conf 存放 pcp 管理端口的管理员账号,pool_passwd 存放后端数据库的登录密码,两个文件都要生成:

pg_md5 --config-file=/etc/pgpool/pgpool.conf --md5

执行后按提示输入密码,生成 md5 加密串写入 pool_passwd。注意 pool_passwd 里的用户是后端 PostgreSQL 用户,不是 pgpool 自己的用户。pcp.conf 用 pgpool 自带的工具生成:

pg_md5 --config-file=/etc/pgpool/pgpool.conf --md5 > /etc/pgpool/pcp.conf

failover 脚本是整套故障转移逻辑的执行者。pgpool 检测到主库宕机后,会以传入参数的方式调用这个脚本,脚本里写清“谁是新主、谁被提升、虚拟 IP 怎么漂”:

#!/bin/bash # failover.sh # 参数: %d 被failover节点ID, %h 被failover节点主机名, %P 新主节点ID, %H 新主主机名 FAILED_NODE_ID=$1 NEW_MASTER_ID=$3 NEW_MASTER_HOST=$4 if [ "$NEW_MASTER_ID" = "1" ]; then # 备库提升为新主 ssh postgres@$NEW_MASTER_HOST "/opt/postgres/pg12/bin/pg_ctl promote -D /data/pgdata" # 虚拟 IP 漂移到新主 ssh root@$NEW_MASTER_HOST "/sbin/ip addr add 192.168.1.100/24 dev eth0" ssh root@$FAILED_NODE_ID "/sbin/ip addr del 192.168.1.100/24 dev eth0" fi

脚本里的 IP 操作需要 root 权限,所以 SSH 免密要同时覆盖 postgres 用户到 postgres 用户、root 到 root 两条链路。很多人的免密只配了 postgres 用户,结果 promote 成功但虚 IP 漂移失败,应用照样连不上数据库。

4.4 启动验证

两个节点的 pgpool 依次启动,然后用 psql 走 9999 端口连上去验证:

pgpool -f /etc/pgpool/pgpool.conf -D psql -h 192.168.1.100 -p 9999 -U postgres -d postgres

-D 表示以 no daemon 模式运行,方便看日志。进入 psql 后执行 show pool_nodes,能看到后端节点列表和状态:

node_id | hostname | port | status | lb_weight | role --------+---------------+------+--------+-----------+------ 0 | 192.168.1.10 | 5432 | up | 0.5 | primary 1 | 192.168.1.11 | 5432 | up | 0.5 | standby

status 为 up、role 区分 primary/standby,说明 pgpool 已经正确识别后端拓扑。如果 standby 节点的角色显示为 unknown,多半是 pgpool 的流复制检查配置缺失,需要确认 sr_host_user 和 sr_host_password 参数是否正确。

5. 避坑排查实录:主从复制与 pgpool 最常见的六个故障

文档里专门有一节“错误”排查,把常见问题按现象、原因、解决列得很清楚。这一章把我拆过的几套环境里真实遇到的高频坑位整理成排错卡片,每一条都是踩过之后才记住的。

5.1 no pg_hba.conf entry for host

现象:备库启动后,日志反复报 FATAL: no pg_hba.conf entry for host "192.168.1.11",流复制建立不起来。

原因:pg_hba.conf 里 replication 类型的记录没写,或者地址段不对。前面提到过 replication 连接是独立类型,必须显式授权。

解决:主库 pg_hba.conf 加一行,然后 reload:

echo "host replication replica_user 192.168.1.0/24 md5" >> /data/pgdata/pg_hba.conf pg_ctl reload -D /data/pgdata

5.2 all backend nodes are down

现象:pgpool 日志显示 all backend nodes are down,客户端连接直接被拒绝。但 PostgreSQL 两个节点的进程明明都活着。

原因:pgpool 的健康检查用户连不上数据库,可能密码不对、pg_hba 没放行,或者健康检查数据库不存在。

解决:先手动用 health_check_user 连一下后端数据库:

psql -h 192.168.1.10 -U pgpool_check -d postgres -c "select 1"

能连上但 pgpool 还是报 down,就用前台模式启动看一下具体报错:

pgpool -n -f /etc/pgpool/pgpool.conf -d

-d 打开 debug 日志,健康检查失败的真实原因会打在 stderr 里。

5.3 同步复制配置后主库写事务卡死

现象:配了 synchronous_commit = on 和 synchronous_standby_names,结果主库的 INSERT 提交后迟迟不返回,等了几十秒才成功。

原因:备库的 walreceiver 没有成功连接,或者连接了但 sync_state 不是 sync。同步复制要求备库确认 WAL 写入后才算提交完成,备库不响应,主库就按配置的等待超时策略挂起。

解决:先查 pg_stat_replication 里的 sync_state,确认是 sync 而不是 async。如果是 async,检查备库 primary_conninfo 里的用户能不能正常连接,再确认主库有没有重启使 synchronous_standby_names 生效。注意 synchronous_standby_names 是 SIGHUP 级参数,reload 就能生效,但改完一定要 confirm:

SELECT * FROM pg_settings WHERE name = 'synchronous_standby_names';

5.4 备库一直处于恢复状态,查询报错

现象:备库能启动,但应用连上去执行 SELECT 报 “cannot execute SELECT in a read-only transaction”。

原因:hot_standby 参数没有设为 on。备库默认不接受任何查询,只能做 WAL 重放。这个问题在 12.2 里默认是 on,但 10.3 里默认 off,不显式配置就会被忽略。

解决:备库 postgresql.conf 里加 hot_standby = on,重启备库。

5.5 pg_basebackup 报权限错误或目录非空

现象:备库执行 pg_basebackup 报 could not open directory "/data/pgdata": Permission denied,或者 directory "/data/pgdata" exists but is not empty。

原因:两件事。第一,pg_basebackup 是以当前系统用户执行的,如果是 root 或其他用户,没有权限写数据目录;第二,data 目录在初始化时被 initdb 写过,或者 mkdir 之后里面有残留文件,pg_basebackup 不允许覆盖非空目录。

解决:确保数据目录为空且属主是 postgres:

rm -rf /data/pgdata mkdir -p /data/pgdata chown postgres:postgres /data/pgdata su - postgres -c "pg_basebackup -h 192.168.1.10 -U replica_user -D /data/pgdata -F p -X stream -R"

5.6 failover 后虚拟 IP 没有漂移

现象:主库宕机,pgpool 完成了 promote,备库已经是 primary,但应用的连接还是中断,ping 虚拟 IP 不通。

原因:failover 脚本里的 IP 漂移逻辑没执行成功。最常见是脚本里的 ssh root 免密没配置,或者 escalation_command 参数没配。还有一个容易被忽略的点:failover 脚本必须要有可执行权限,pgpool 调用它时如果权限不对,脚本直接不执行,日志里也未必有明确报错。

解决:检查三点,脚本可执行权限、root 免密、escalation_command 配置。

chmod 755 /etc/pgpool/failover.sh chmod 755 /etc/pgpool/escalation.sh

然后手动执行一次脚本,确认 IP 命令本身没有语法错误。很多时候脚本里 IP 网段写错,或者是绑定的网卡名写错,手动跑一遍立刻就能暴露。

6. 切换测试与 pg_rman 备份:高可用最后一道验证

整套系统搭完,最后一步不是验收,是把主库往死里整一次。关掉主库的 postgres 进程,观察 pgpool 是否在健康检查周期内发现节点 down,是否自动触发 promote,虚拟 IP 是否漂移,应用连接是否恢复正常。这个测试不做,你根本不知道 failover 脚本是不是纸面配置。

pg_rman 的初始化和备份逻辑相对独立,但它和 pgpool 的 failover 一样,属于“平时用不到、用到救命”的部件。初始化一次:

pg_rman init -B /backup/pg_rman

归档模式必须开。主库 postgresql.conf 里设置 archive_mode = on,archive_command 用 pg_rman 的话,可以直接把归档交给备份工具管理:

pg_rman backup --backup-mode=full --pgdata=/data/pgdata -B /backup/pg_rman pg_rman backup --backup-mode=archive --pgdata=/data/pgdata -B /backup/pg_rman

备份脚本按文档里的两个示例,一个处理全量备份,一个处理归档备份,配合 crontab 定时执行。全量备份存的是数据目录的物理快照,归档备份存的是 WAL 日志增量,两者结合才能做到任意时间点恢复。

我做这套方案验收时吃过一次亏:failover 测试做完,备库 promote 成新主,但原主库恢复后直接加回集群,没有重新初始化成新主的新备库。结果原主库的 WAL 与现主库不一致,pgpool 把两个节点都标记为 down,高可用彻底失效。从那以后,每次切换测试我都强制走一遍完整流程:备库 promote 之后,原主库必须做 pg_basebackup 重拉数据目录,重新成为备库,再验证 pgpool 节点状态恢复正常,才算测试通过。这套方案的价值不在配置写得多漂亮,而在故障发生时能不能按预期兜住。希望这份拆解帮你在自己环境里把每一步都跑通、验证完,让高可用不再是文档上的概念。

本文还有配套的精品资源,点击获取

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

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

立即咨询