1. 为什么要搭这套集群
先把话放前面:数据库单点,早晚出事。别管你是跑一个小网站还是支撑核心业务,只要 PostgreSQL 还挂在一台机器上,那么硬盘故障、内核 panic、机房断电、运维误操作,任何一件小事都能让服务直接停摆。所以我一直建议,数据库层的高可用不是可选项,是刚需。
pgpool-II 是 PostgreSQL 生态里一个老牌中间件,它同时干几件事:连接池、负载均衡、自动故障转移。和 Patroni 这类偏“数据库自治”的方案相比,pgpool-II 更轻量,部署直观,适合中小规模团队快速搭起一套“读写分离 + 主备自动切换”的架构。我这次用的是 pgpool-II 4.4 系列,PostgreSQL 用的是 14,这两者配合非常成熟,网上踩坑资料也多,适合作为生产环境的基线版本。
这套方案能解决什么问题?简单说:应用只管连 pgpool 的虚拟 IP,完全不用关心后端哪台是主、哪台是备。主库挂了,pgpool 自动把备库提升为新主库,应用几乎无感知。读请求能在主备之间分发,减轻主库压力。连接被 pgpool 统一管理,避免应用直连数据库导致连接数被打爆。
适合谁来参考?正在用 PostgreSQL 做业务库、想解决单点故障的小团队,或者刚接手数据库运维、想系统学一下高可用机制的开发者。文章里我会把架构设计、配置步骤、故障转移验证、常见坑全部过一遍。
2. 架构设计与资源规划
2.1 拓扑结构
我这边采用两节点方案:一台主库、一台备库,中间放两台 pgpool 做守护,为什么要两台 pgpool?因为 pgpool 本身也有单点问题。如果只有一台 pgpool,它挂了,后端数据库再健康,应用也连不上。所以生产环境至少两台 pgpool,配合虚拟 IP(VIP)+ keepalived 做主备切换,才能保证整个接入层也是高可用的。
整体拓扑如下:
- 应用连接 VIP(比如 192.168.1.100),VIP 由两台 pgpool 节点共享
- pgpool 节点 1(192.168.1.11)和节点 2(192.168.1.12)互为主备
- 后端 PostgreSQL 主库(192.168.1.21)和备库(192.168.1.22)通过流复制保持数据同步
- pgpool 负责把读请求分发到主备,写请求强制走主库
在这个架构里,数据流向是:应用 → keepalived VIP → pgpool → PostgreSQL。任何一个环节失效,都有对应的切换机制兜底。
2.2 主机规划
我建议所有节点都使用独立物理机或虚拟机,不要搞容器化一锅炖,数据库这种有状态的服务,先跑在裸机或 VM 上最踏实。我的规划如下:
| 主机名 | IP地址 | 角色 | 配置建议 |
|---|---|---|---|
| pg-node1 | 192.168.1.21 | PostgreSQL 主库 | 4核8G,SSD 100G |
| pg-node2 | 192.168.1.22 | PostgreSQL 备库 | 4核8G,SSD 100G |
| pgpool-node1 | 192.168.1.11 | pgpool 主节点 + keepalived MASTER | 2核4G |
| pgpool-node2 | 192.168.1.12 | pgpool 备节点 + keepalived BACKUP | 2核4G |
操作系统我选的是 CentOS 7.9,虽然不是最新,但胜在稳定,pgpool 官方仓库对它的支持一直很好。如果你的环境是 Rocky Linux 9 或者 Ubuntu 22.04,配置逻辑一样,就是包管理器命令略有差异。
2.3 版本选择说明
PostgreSQL 14 和 pgpool-II 4.4 是我目前比较放心的一对组合。PostgreSQL 14 的流复制已经很成熟,pgpool 4.4 对 PostgreSQL 14 的pg_rewind支持也很好,故障切换后老主库重新加入集群时,可以通过pg_rewind快速同步增量数据,不需要重建整个备库。
这个点很重要。早期版本的 pgpool 配合 PostgreSQL 处理老主库回归时,常常要全量重建,数据量大时非常痛苦。有了pg_rewind,老主库能像“增量备份恢复”一样追上新主库,速度快很多。
3. PostgreSQL 主备流复制搭建
3.1 基础环境配置
两台 PostgreSQL 节点都需要做同样的基础配置。首先关闭防火墙或者放行端口,我建议直接放行,测试环境别在防火墙层面浪费时间,生产环境按公司安全规范操作:
systemctl stop firewalld systemctl disable firewalld setenforce 0 sed -i 's/^SELINUX=.*/SELINUX=disabled/' /etc/selinux/config然后配置 hosts,确保节点之间可以通过主机名互相解析:
cat >> /etc/hosts <<EOF 192.168.1.21 pg-node1 192.168.1.22 pg-node2 192.168.1.11 pgpool-node1 192.168.1.12 pgpool-node2 EOF3.2 安装 PostgreSQL 14
我用的是 PostgreSQL 官方 YUM 仓库,安装过程不复杂:
yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm yum install -y postgresql14-server postgresql14-contrib装完后初始化数据目录。注意 PostgreSQL 14 使用initdb初始化时,默认会创建一个超级用户,我建议先设置好密码再初始化,省得后面再改:
/usr/pgsql-14/bin/postgresql-14-setup initdb systemctl start postgresql-14 systemctl enable postgresql-14接着设置 postgres 用户密码:
su - postgres psql -c "ALTER USER postgres PASSWORD 'YourStrongPassword';"3.3 主库配置
主库的配置核心是开启归档和流复制。编辑/var/lib/pgsql/14/data/postgresql.conf:
listen_addresses = '*' port = 5432 wal_level = replica max_wal_senders = 10 wal_keep_size = 1024 archive_mode = on archive_command = 'test ! -f /backup/pgwal/%f && cp %p /backup/pgwal/%f'这里解释一下几个关键参数:
max_wal_senders:允许多少个备库连接主库拉取 WAL,2 个节点就够,我设 10 是为了留余量wal_keep_size:主库保留多少 WAL 供备库追赶,如果备库断连太久,WAL 被回收就需要重新全量同步,1024MB 是个比较稳妥的初始值archive_command:归档 WAL 到本地目录,这个在故障切换后有用,保证数据不丢
然后配置pg_hba.conf,允许备库和 pgpool 节点连接:
host replication postgres 192.168.1.0/24 md5 host all postgres 192.168.1.11/32 md5 host all postgres 192.168.1.12/32 md5 host all all 192.168.1.0/24 md5这里要特别提醒:replication那条记录是给流复制用的,pgpool 的健康检查走的是普通连接。千万别把这两类权限搞混,不然故障转移时 pgpool 会报权限不足。
3.4 备库搭建
备库搭建的第一步是从主库做一次全量基础备份。在备库上执行:
su - postgres /usr/pgsql-14/bin/pg_basebackup -h pg-node1 -U postgres -D /var/lib/pgsql/14/data -P -R -X stream几个参数的含义:
-R:自动生成 standby.signal 文件和 recovery 配置,这样备库启动时就知道自己是一个待命节点-X stream:通过流复制方式传输 WAL,不需要等归档,速度更快
执行成功后,journalctl 确认备库能正常启动:
systemctl start postgresql-14 systemctl enable postgresql-14这时候在主库执行psql -c "select * from pg_stat_replication;",应该能看到一条备库的连接记录。看到state: streaming就说明流复制已经跑起来了。
3.5 验证流复制
建一张测试表,插入数据,然后分别在主备上查询,确认数据一致:
-- 主库执行 CREATE TABLE tb_test (id int primary key, name text); INSERT INTO tb_test VALUES (1, 'hello');备库执行查询:
SELECT * FROM tb_test;如果备库能查到(1, 'hello'),说明同步链路正常。这个步骤不能省,很多配置错误要到故障切换时才暴露,那就晚了。
4. 安装与配置 pgpool-II
4.1 安装 pgpool-II
两台 pgpool 节点都安装 pgpool 和 keepalived:
yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm yum install -y pgpool-II-14 keepalived注意这里是pgpool-II-14,这个 14 代表它匹配 PostgreSQL 14 的扩展版本,不要装成 13 或者 15,版本不对会有兼容性问题。
安装完成后,pgpool 的配置文件在/etc/pgpool-II/,主要文件有:
pgpool.conf:主配置文件pcp.conf:管理接口认证文件pool_hba.conf:客户端认证配置
4.2 pgpool.conf 核心配置
先不啰嗦,直接给一份能跑通的配置,然后逐段解释:
listen_addresses = '*' port = 9999 socket_dir = '/var/run/pgpool' pcp_listen_addresses = '*' pcp_port = 9898 backend_hostname0 = 'pg-node1' backend_port0 = 5432 backend_weight0 = 1 backend_data_directory0 = '/var/lib/pgsql/14/data' backend_flag0 = 'ALLOW_TO_FAILOVER' backend_hostname1 = 'pg-node2' backend_port1 = 5432 backend_weight1 = 1 backend_data_directory1 = '/var/lib/pgsql/14/data' backend_flag1 = 'ALLOW_TO_FAILOVER' enable_pool_hba = on pool_passwd = 'pool_passwd' num_init_children = 32 max_pool = 4 child_life_time = 300 connection_life_time = 0这一段是基础连接配置。backend_hostname0和backend_hostname1定义了两个后端节点,pgpool 会按编号 0 和 1 来区分主备。注意,backend_data_directory必须填对,pgpool 在故障切换时要读这个目录去执行pg_rewind,填错路径会导致切换失败。
然后配置健康检查和故障转移:
health_check_period = 5 health_check_timeout = 3 health_check_user = 'postgres' health_check_password = 'YourStrongPassword' health_check_max_retries = 3 health_check_retry_delay = 1 failover_command = '/etc/pgpool-II/failover.sh %d %h %p %D %m %H %M %P'健康检查每 5 秒执行一次,如果连续 3 次失败,就触发 failover 脚本。这个频率是平衡了“故障发现速度”和“避免误判”的结果,太快容易抖动误判,太慢业务受影响时间长。
再配置自动恢复和 watchdog(两台 pgpool 之间的协调机制):
auto_failback = on failback_command = '/etc/pgpool-II/failback.sh %h %H %M' use_watchdog = on trusted_servers = 'pgpool-node1,pgpool-node2' delegate_IP = '192.168.1.100' if_cmd_path = '/sbin' if_up_cmd = 'ip addr add $_IP_/24 brd 192.168.1.255 dev eth0 label eth0:0' if_down_cmd = 'ip addr del $_IP_/24 dev eth0' heartbeat_device = 'eth0' wd_hostname = 'pgpool-node1' wd_port = 9000 wd_hb_interval = 2 wd_life_point = 5 wd_lifecheck_query = 'SELECT 1'watchdog 是 pgpool 自带的高可用机制,它和 keepalived 配合,负责 VIP 的漂移。这里delegate_IP就是应用连接数据库时用的虚拟 IP,两台 pgpool 谁会绑定这个 IP,谁就是当前活跃的接入节点。
主备切换这一步很重要。watchdog 本身能保证 pgpool 节点的高可用,但 VIP 的漂移动作它需要系统命令配合,所以还需要 keepalived 或者手动脚本。我用的是 keepalived + 脚本组合,后面细说。
4.3 配置 pcp.conf
pcp 协议是 pgpool 的管理接口,用于手动执行切换、查看节点状态等操作。创建 pcp 用户:
PGPOOL_SETUP=1 /usr/pgpool-14/bin/pg_md5 YourPCPPassword >> /etc/pgpool-II/pcp.conf echo 'pgpool:YourPCPPassword' >> /etc/pgpool-II/pcp.conf注意,pg_md5生成的只是密码的 MD5 值,不是明文。pcp.conf 里的格式是“用户名:MD5密码”,别把明文写进去。
4.4 配置 pool_hba.conf
为了保证客户端认证一致,我直接复用 PostgreSQL 的 hba 文件逻辑:
host all all 192.168.1.0/24 md5如果想在 pgpool 层面做更细粒度的权限控制,可以自己加规则,但test环境这样已经够用。
4.5 配置前端认证
pgpool 自身有一套密码认证体系,它需要从 PostgreSQL 里读取用户信息并生成 pool_passwd 文件:
/usr/pgpool-14/bin/pg_md5 -p -m -u postgres YourStrongPassword mv pool_passwd /etc/pgpool-II/pool_passwd chown postgres:postgres /etc/pgpool-II/pool_passwd这里我用-u postgres指定了要认证的用户名,密码要和 PostgreSQL 里的一致,否则客户端连接时认证失败。
4.6 编写 failover.sh 和 failback.sh
这两个脚本是故障转移的关键。failover.sh 在主库不可用时触发,它负责把备库提升为新主库,并通知 pgpool 更新节点状态:
#!/bin/bash # $1 = failed node id, $2 = failed hostname, $3 = failed port, # $4 = failed data dir, $5 = new main node id, $6 = new main hostname, # $7 = new main port, $8 = new main data dir PGDATA_DIR="/var/lib/pgsql/14/data" PGBIN="/usr/pgsql-14/bin" LOG_FILE="/var/log/pgpool/failover.log" echo "$(date) failover triggered: node $1 ($2:$3) failed, promote $6" >> $LOG_FILE if [ $1 -eq 0 ]; then # 主库挂了,提升备库 ssh postgres@$6 "$PGBIN/pg_ctl promote -D $8" sleep 5 exit 0 fi这个脚本里有个细节:没有用 pgpool 的pcp_promote_node而是直接用pg_ctl promote,原因是我希望提升动作在数据库实例层面真正完成,而不是只在 pgpool 状态表里标记一下。两者区别在于:如果只是标记 pgpool 状态,而 PostgreSQL 还没真正执行 promote,那么后续写请求进来会落到一个仍处于只读状态的实例上,直接报错。
failback.sh 在老主库恢复后把它重新加入集群。这里只做简单记录,具体的数据追平需要配合pg_rewind,我会在常见问题部分详细说明:
#!/bin/bash # $1 = failed host, $2 = new main host, $3 = new main port LOG_FILE="/var/log/pgpool/failback.log" echo "$(date) failback: node $1 rejoins cluster, current main is $2" >> $LOG_FILE exit 0生产环境我会在 failback 脚本里加上pg_rewind的自动执行逻辑,但这一步对新手来说风险较高,建议先在测试环境验证好再上。
5. keepalived 实现 VIP 漂移
5.1 keepalived 配置
pgpool 节点 1 作为 keepalived MASTER,节点 2 作为 BACKUP。节点 1 的配置:
global_defs { router_id pgpool_master } vrrp_script check_pgpool { script "/etc/keepalived/check_pgpool.sh" interval 2 weight -20 } vrrp_instance VI_1 { state MASTER interface eth0 virtual_router_id 51 priority 100 advert_int 1 authentication { auth_type PASS auth_pass your_auth_pass } track_script { check_pgpool } virtual_ipaddress { 192.168.1.100/24 dev eth0 label eth0:0 } }节点 2 的配置基本一样,把state MASTER改成BACKUP,priority改成90,其他参数一致。
5.2 健康检查脚本
keepalived本身只负责VIP漂移,它需要知道pgpool到底还活着没有。我写了一个简单的检测脚本:
#!/bin/bash if [ $(ps -ef | grep -c "[p]gpool") -eq 0 ]; then exit 1 fi exit 0如果 pgpool 进程不存在,keepalived的权重会降低,VIP 自动漂移到另一台 pgpool 节点。
这个方案的优点是简单直接,缺点是只检测了进程是否存在,没有检测 pgpool 是否真的能对外提供服务。更严格的检测可以加一条 TCP 连接测试,比如用nc -z localhost 9999检查端口,但要注意检测频率别太高,否则 keepalived 会频繁抖动。
6. 启动与验证
6.1 启动顺序
启动有个先后顺序,这也是新手最容易搞混的地方:
- 先启动两台 PostgreSQL,确保主备流复制正常
- 启动两台 pgpool,先主后备,让 watchdog 正常建立通信
- 启动 keepalived,让 VIP 绑定到节点 1
# 1. PostgreSQL节点 systemctl start postgresql-14 # 2. pgpool节点 systemctl start pgpool systemctl enable pgpool # 3. keepalived节点 systemctl start keepalived systemctl enable keepalived6.2 检查 pgpool 状态
在 pgpool 节点上执行:
psql -h 192.168.1.100 -p 9999 -U postgres -c "show pool_nodes;"如果配置正确,应该看到类似下面的输出:
node_id | hostname | port | status | lb_weight | role --------+-----------+------+--------+-----------+--------- 0 | pg-node1 | 5432 | up | 0.5 | primary 1 | pg-node2 | 5432 | up | 0.5 | standbystatus为up,role一个primary一个standby,这就是理想状态。如果status显示down,说明 pgpool 和后端 PostgreSQL 的健康检查失败了。
再检查一下 VIP 是否生效:
ip addr show eth0 | grep 192.168.1.100能看到这个 IP 就说明 keepalived 工作正常。
6.3 应用连接方式
应用连接字符串应该指向 pgpool 的 VIP 和 9999 端口:
jdbc:postgresql://192.168.1.100:9999/appdb这里有个容易踩的坑:应用连的是 VIP,不是具体某台 pgpool,也不是后端的 PostgreSQL 节点。如果应用直连了后端数据库,那高可用就白搭了,后端一切换,应用立刻断连。
7. 故障转移实测
7.1 模拟主库宕机
最直接的测试方法就是把主库的 PostgreSQL 进程杀掉:
# 在主库节点 pg-node1 上执行 pkill -9 postgres模拟的是最强故障——进程被直接杀死,没有优雅退出。
7.2 观察故障转移过程
然后观察 pgpool 日志,看故障转移是否被正确触发:
tail -f /var/log/pgpool/pgpool.log正常情况下会看到类似以下日志序列:
ERROR: failed to connect to PostgreSQL server on pg-node1:5432 LOG: health check failed on node 0 LOG: executing failover command LOG: failover: promoting node 1 to primary LOG: failover: done等几秒钟,再执行:
psql -h 192.168.1.100 -p 9999 -U postgres -c "show pool_nodes;"这时候应该看到node_id 1的状态变成了primary,node_id 0变成了down。故障切换完成。
7.3 应用无感验证
我在测试环境跑了一个持续写入的压力脚本,连接字符串指向 VIP。观察到的现象是:主库宕机瞬间,写入请求会报错,但这个报错窗口不超过 15 秒,之后新连接自动落到新主库上,事务继续正常提交。如果应用层做了连接重试机制,整个故障转移过程用户根本感知不到。
这个 15 秒的窗口主要包含:健康检查发现故障(最多5秒)、触发 failover 脚本(几秒)、pgpool 更新节点状态(瞬时)。如果希望窗口更短,可以把health_check_period调小到 2 秒,但要注意加大误判风险,需要根据实际网络状况权衡。
7.4 老主库恢复加入集群
这是很多教程没讲清楚的部分。主库恢复之后,不能直接把它启起来加回集群,因为它身上已经没有新数据了,而且它原来的身份是主库,现在整个集群的主库已经是 pg-node2 了。直接加入会导致数据不一致。
正确做法分两步,先用pg_rewind让老主库追平新主库的数据,再把它作为备库重新加入:
# 在 pg-node1 上执行,pg-node2 现在是主库 /usr/pgsql-14/bin/pg_rewind --target-pgdata=/var/lib/pgsql/14/data \ --source-server='host=pg-node2 port=5432 user=postgres dbname=postgres'执行成功后,创建 standby 信号文件:
touch /var/lib/pgsql/14/data/standby.signal然后修改postgresql.auto.conf,添加连接新主库的信息:
primary_conninfo = 'host=pg-node2 port=5432 user=postgres password=YourStrongPassword'最后启动 PostgreSQL:
systemctl start postgresql-14启动后,pgpool 的健康检查会自动发现老主库恢复了,把它加入后备节点列表。再执行show pool_nodes,应该看到两个节点都是up,主库是 pg-node2,备库是 pg-node1。
这整条链路我在生产环境验证过一次:老主库从断电到重新加入集群,总耗时大约 10 分钟,其中大部分时间花在人工确认数据一致性上。如果数据量不大,整个过程可以压缩到 5 分钟以内。
8. 常见问题与排障实录
8.1 pgpool 节点显示 down
现象:show pool_nodes里面某个节点的状态是down,但 PostgreSQL 进程是正常的。
排查方向:
- 确认 pgpool 健康检查使用的用户名密码正确。pgpool 是用
health_check_user去连接后端数据库的,密码错了会导致健康检查一直失败 - 检查 pg_hba.conf 里是否允许 pgpool 所在 IP 连接。注意,健康检查用的也是普通连接,不是 replication 连接,所以要在
pg_hba.conf里配置对应的 host 记录 - 检查网络连通性。用
telnet或者nc从 pgpool 节点测后端数据库的 5432 端口通不通
8.2 故障切换后原主库不能重新加入
现象:原主库通过pg_rewind同步数据后启动,发现和当前主库数据还是不一致,或者干脆启动失败。
原因分析:pg_rewind要求原主库开启了wal_log_hints = on或者checksum,否则它无法判断哪些数据块需要重放,会报错退出。
解决方案:在 PostgreSQL 14 的配置里加上:
wal_log_hints = on然后重启生效。这是我在生产环境踩过最痛的坑,如果不提前开启这个参数,等需要pg_rewind的时候再改,必须重启数据库,那就失去“无缝回归”的意义了。
8.3 pgpool 脑裂
现象:两台 pgpool 同时绑定 VIP,都认为自己才是 MASTER,应用请求被随机分发到两台 pgpool 上,后端的写请求可能出现冲突。
排查方向:
- 检查两台 pgpool 之间的 heartbeat 通信。watchdog 使用
wd_hb_interval配置的心跳包维持状态同步,如果网络不通,它们会彼此认为对方挂了,从而同时抢占 VIP - 检查 keepalived 的认证配置。
auth_pass不一致会导致 keepalived 节点间无法同步状态 - 检查
delegate_IP是否被其他设备占用,如果这个 IP 已经绑到了别的地方,两台 pgpool 都会抢不到,或者一个能抢到一个不能
解决思路:在 keepalived 的检测脚本里增加“检查对方是否存活”的逻辑。比如本轮检测如果发现自己能 ping 通对方,并且对方也在广播 VIP,就主动放弃 VIP。
8.4 连接数被打满
现象:应用侧频繁报连接超时,pgpool 日志里出现sorry, too many clients already。
原因分析:num_init_children限制了 pgpool 可以同时接受的客户端连接数。这个值不是越大越好,它的底层逻辑是每个子进程都要消耗系统资源,和 PostgreSQL 的max_connections也不是一一对应的。因为 pgpool 还有连接池功能,一个后端连接可以被多个客户端复用。
优化建议:
- 把
num_init_children调大,比如 64 或者 128,但前提是系统内存够 - 确认应用端是否设置了连接池。如果应用本身有连接池(比如 Java 的 HikariCP),连接数应该是可控的
- 检查是否有慢查询占用了连接池里的连接不释放。
child_life_time可以限制子进程的最大存活时间,让异常连接自动回收
我个人推荐num_init_children设置为 2~3 倍的预期并发数,再配合max_pool = 4,这样连接池的使用效率最高。
8.5 读写分离不生效
现象:show pool_nodes显示两个节点都是up,执行查询也确实能返回数据,但所有请求都打到主库上,备库没有承担任何读流量。
原因分析:pgpool 的负载均衡不是无条件的。它要求请求的会话不能绑定到某个后端节点,同时查询不能修改数据。具体来说,以下场景会导致负载均衡失效:
- 会话开启了事务,且事务内没有使用只读语句
- 查询语句被显式标记为需要路由到主库
statement_level_load_balance没有开启
优化配置:
statement_level_load_balance = on开启这个参数后,即使在一个会话里连续执行多条只读 SQL,pgpool 也会尝试在多个节点之间分散请求。
8.6 pcp 命令连接失败
现象:执行pcp_node_count等命令时提示认证失败。
排查方向:pcp 认证用的不是 PostgreSQL 的密码,而是pcp.conf里的密码。确认你生成密码 MD5 时用的命令和格式正确:
/usr/pgpool-14/bin/pg_md5 YourPassword然后把输出追到/etc/pgpool-II/pcp.conf里,格式是“用户名:MD5值”。
还要注意,pcp 命令默认也要走认证,如果用pcp_node_count -h 127.0.0.1 -U pgpool,它会提示输入密码,这个密码就是pcp.conf里的那个。
9. 优化与监控建议
9.1 延迟与数据一致性
pgpool 的负载均衡把读请求分发到备库,但备库的数据和主库之间存在流复制延迟。如果业务对读一致性要求极高(比如用户刚提交订单立刻查状态),这种读写分离架构就会有问题。
解决方案有二:
- 让这类关键读请求强制走主库。pgpool 提供了
/*NO LOAD BALANCE*/注释标记,应用可以在 SQL 前加这个注释,pgpool 就会把这条查询路由到主库 - 设置
delay_threshold,超过阈值的备库自动从负载均衡中剔除
delay_threshold = 1000000这个值单位是字节,表示备库 WAL 日志落后主库超过 1MB 时,就不再参与读负载均衡。
9.2 pgpool 节点自身的监控
pgpool 挂了,VIP 会自动漂移,但运维人员需要第一时间知道。我在生产环境里用简单的 shell 脚本 + 监控平台(比如 Zabbix 或 Prometheus)做告警。脚本核心逻辑:
#!/bin/bash # 检测 pgpool 是否活着 && 当前 VIP 是否绑定正确 pgpool_alive=$(ps -ef | grep -c "[p]gpool") vip_exists=$(ip addr show eth0 | grep -c "192.168.1.100") if [ $pgpool_alive -eq 0 ]; then echo "pgpool is down" exit 1 fi if [ $vip_exists -eq 1 ]; then echo "pgpool is alive, VIP present" else echo "pgpool is alive, but VIP not on this node" fi监控的意义在于:主备切换虽然自动完成了,但如果你不知道发生了切换,可能会在业务出现异常时找不到方向。每次切换都应该有告警,让运维人员介入确认。
9.3 连接池参数调优
给一个我常用的参数模板:
| 参数 | 建议值 | 说明 |
|---|---|---|
| num_init_children | 64 | 按业务预估并发数调整 |
| max_pool | 4 | 每个子进程到后端的连接池大小 |
| child_life_time | 300 | 300秒后回收子进程,防止内存泄漏 |
| connection_life_time | 0 | 0表示永久有效,如需定期重连可设置1800 |
| client_idle_limit | 0 | 客户端空闲多少秒后断开,0表示不断开 |
参数调整不是一劳永逸的,建议每隔一段时间观察 pgpool 的进程数和内存占用,再结合业务高峰期的表现微调。
10. 个人实践心得
这套 PostgreSQL + pgpool 高可用集群,我从测试到生产已经维护了大半年,整体稳定性是靠谱的。但有几个建议想分享给后来者:
第一,先在测试环境完整演练至少三次故障切换,包括主库宕机、备库宕机、pgpool 节点宕机、网络分区这四类场景。我在测试阶段发现过一个问题:主库宕机时,如果备库的 WAL 归档路径和主库不一致,pg_rewind会失败。这种问题只有多演练才能暴露。
第二,pgpool 的配置项非常多,别追求一次配到完美。先把最核心的链路走通——客户端连得上、读写分离、故障转移能切换——然后再逐项优化负载均衡策略、连接池参数、健康检查频率。一上来就想配一个“完美”的配置,往往会被大量参数淹没。
第三,日志和监控一定要早点接入。pgpool 自身日志、系统日志、数据库日志,三者缺一不可。我见过很多团队,部署得好好的,等到出故障了才想起来翻日志,结果发现日志没配全,追溯起来非常费劲。
第四,关于“自动恢复加入集群”,我建议在运维流程里加一道“人工确认”的关卡:老主库恢复后,先让它作为备库加入,观察半小时,确认数据追平、复制延迟稳定,再把它重新纳入负载均衡。不是所有故障都需要立即恢复,稳定比速度更重要,尤其在金融、电商这类对一致性敏感的业务里。
最后说一个细节:pgpool 的配置文件里到处是路径、用户名、密码,改完配置一定要重启服务并确认日志里没有报错。我习惯改完配置后执行一次pgpool reload(不是 restart),然后看日志确认配置没有语法错误,再决定是否重启。reload和restart的区别在于,reload 会重新读取配置但不中断现有连接,restart 会清空所有连接池。生产环境尽量用 reload,减少对业务的影响。
如果你正在折腾这套架构,希望这篇文章能帮你少踩几个坑。有问题多观察日志,多模拟故障,多验证行为,系统会慢慢给你答案。