简介:这份文档面向 PostgreSQL 数据库运维与架构人员,聚焦主从流复制与 pgpool-II 高可用方案的落地实践。内容从流复制原理切入,讲解 WAL 日志如何由 walsender 实时传输至备库 walreceiver 并回放同步,并对比同步复制与异步复制在数据一致性和写性能上的取舍;随后展开 pgpool-II 的连接池、复制、负载均衡与读写分离、连接数限制等能力,说明主库故障时的自动切换思路。文档还覆盖实践环境规划、软件版本准备、postgres 用户与权限设置、主库与备库配置、pgpool-II 参数调整及启动测试等环节,并涉及防火墙、SELinux、hosts、时钟同步、sysctl 与 limit 等系统资源调整,目录结构按方案介绍、实践环境、环境准备、资源调整等章节组织,便于按步骤查阅。资源包为 1 个 docx 文件,约 773KB,已有 469 人学习,适合需要搭建主备高可用架构的读者参考。
1. 从一台库扛不住说起:Postgres主从流复制加pgpool到底解决什么问题
很多团队第一次认真考虑数据库高可用,不是因为架构评审,而是因为某个凌晨主库连接数打满、磁盘IO跑满,业务全线报错。单台Postgres再能扛,也顶不住硬件故障、误操作和突发流量这三件事。标题里的方案,本质是用两条腿走路:Postgres原生的主从流复制负责数据实时同步,pgpool-II夹在应用和数据库之间负责连接路由、故障切换和读写分离。它不引入额外存储引擎,不依赖外部协调服务,纯靠Postgres自带能力加一个中间件,就能把单点变成可切换的双机甚至多机结构。
这套组合适合谁?适合已经用Postgres、数据量在几百GB以内、团队没有专职DBA但有一定运维能力的中小规模系统。它不适合超大规模分片场景,也不适合对切换时间要求到秒级以下的金融核心。但如果你想要一个能落地、能看懂、能自己排查的高可用方案,主从流复制加pgpool是性价比很高的一条路。下面从原理到配置到踩坑,一步步拆开讲。
2. 主从流复制怎么搭:从参数到验证的完整链路
2.1 流复制的三种模式与选型理由
Postgres流复制按同步策略分三档。异步复制是默认行为,主库提交事务后不等备库确认就返回,性能最好,但主库宕机时可能丢最后几个事务。同步复制要求至少一个备库确认收到WAL才返回,数据不丢,但备库延迟会直接拖慢主库写入。还有一种叫远程应用(remote apply),要求备库实际应用完WAL才确认,一致性最强,延迟也最大。
我一般推荐生产环境用synchronous_commit = remote_write配合synchronous_standby_names指定一个备库。这样主库等备库写入操作系统缓存即可,不强制刷盘,兼顾了数据安全和写入性能。如果业务对丢失零容忍,再考虑on或remote_apply。选型时先问自己一个问题:丢一个事务和写入延迟翻倍,哪个更不能接受。
2.2 主库配置:四个必须改的参数
主库的postgresql.conf里,下面这几个参数是流复制的基础,缺一个都跑不起来。
# 主库 postgresql.conf 关键配置 listen_addresses = '*' # 允许备库从其他机器连接 wal_level = replica # 至少是replica,logical可支持逻辑复制 max_wal_senders = 10 # 允许的WAL发送进程数,按备库数量加余量 wal_keep_size = 1GB # 保留WAL的大小,防止备库落后太多被删 synchronous_standby_names = 'standby1' # 同步备库名,异步复制时注释掉wal_level从minimal改成replica需要重启数据库,这是硬性要求。max_wal_senders默认是10,如果备库多或者有级联复制,要往上加。wal_keep_size在PG13之后替代了wal_keep_segments,设太小备库断连久了就得重建。synchronous_standby_names里的名字要和备库primary_conninfo里的application_name对上,否则同步复制不生效。
改完主库还要配pg_hba.conf,允许备库的IP以复制身份连接。
# 主库 pg_hba.conf 追加 host replication repl_user 192.168.1.0/24 scram-sha-256这里repl_user是专门用于复制的账号,不要用超级用户。创建方式:
-- 在主库执行 CREATE ROLE repl_user WITH REPLICATION LOGIN PASSWORD 'your_password';2.3 备库搭建:pg_basebackup一条命令起步
备库不需要手动初始化数据目录,用pg_basebackup从主库拉一份基础备份最稳妥。
# 在备库执行,先停掉本地Postgres pg_basebackup -h 192.168.1.10 -p 5432 -U repl_user -D /var/lib/pgsql/data -Fp -Xs -P -R参数含义:-h主库地址,-D备库数据目录,-Fp输出普通文件格式,-Xs流式传输WAL,-P显示进度,-R自动生成standby.signal和postgresql.auto.conf里的primary_conninfo。-R是关键,省去手动写恢复配置。执行完检查备库数据目录下是否有standby.signal文件,有就说明备库模式已就绪。
备库的postgresql.conf通常不需要大改,但建议显式设置:
# 备库 postgresql.conf hot_standby = on # 允许备库只读查询 primary_conninfo = 'host=192.168.1.10 port=5432 user=repl_user password=your_password application_name=standby1'application_name要和主库synchronous_standby_names里写的一致。启动备库后,在主库执行SELECT * FROM pg_stat_replication;,能看到一条state = streaming的记录,就说明流复制通了。
2.4 验证复制延迟与切换演练
复制通了不代表没问题,延迟要持续监控。在主库查:
-- 主库查看复制状态 SELECT client_addr, state, sent_lsn, write_lsn, flush_lsn, replay_lsn, (sent_lsn - replay_lsn) AS replay_delay FROM pg_stat_replication;replay_delay是备库回放落后主库的WAL字节数,持续增长说明备库跟不上。备库上查:
-- 备库查看是否在恢复中 SELECT pg_is_in_recovery(); -- 返回t表示备库正常切换演练是必须做的。手动切换流程:停主库,备库执行pg_ctl promote -D /var/lib/pgsql/data,备库变成新主库。然后把旧主库重建为备库。这个过程pgpool可以自动化,但手动演练一遍能让你在真出问题时心里有底。
3. pgpool-II怎么配:连接路由、健康检查与自动切换
3.1 pgpool的核心角色与后端节点定义
pgpool-II站在应用和Postgres之间,应用连pgpool的端口,pgpool再连后端Postgres。它主要做四件事:连接池复用、读写分离路由、健康检查、故障切换。配置从pgpool.conf开始,先定义后端节点。
# pgpool.conf 后端节点定义 backend_hostname0 = '192.168.1.10' backend_port0 = 5432 backend_weight0 = 1 backend_flag0 = 'ALWAYS_PRIMARY' backend_hostname1 = '192.168.1.11' backend_port1 = 5432 backend_weight1 = 1 backend_flag1 = 'DISALLOW_TO_FAILOVER'backend_flag0设为ALWAYS_PRIMARY表示节点0永远是主库候选,节点1设为DISALLOW_TO_FAILOVER防止备库被误提升。backend_weight控制读查询分发权重,备库可以设大一点分担读流量。
3.2 健康检查与故障切换参数
健康检查是pgpool判断后端死活的依据,配不好会出现误切换。
# 健康检查配置 health_check_period = 10 health_check_timeout = 20 health_check_user = 'health_user' health_check_password = 'health_pass' health_check_database = 'postgres' failover_command = '/etc/pgpool-II/failover.sh %d %H %P'health_check_period是检查间隔秒数,10秒比较稳妥。health_check_timeout要大于正常查询时间,否则网络抖动就触发切换。failover_command是切换时执行的脚本,%d是失效节点ID,%H是失效主机名,%P是旧主库ID。脚本里通常做两件事:提升备库、通知pgpool重新配置。
3.3 读写分离与负载均衡配置
读写分离靠load_balance_mode和master_slave_mode两个开关。
master_slave_mode = on load_balance_mode = on master_slave_sub_mode = 'stream'master_slave_sub_mode = 'stream'告诉pgpool后端是流复制结构。这样SELECT会自动分发到备库,INSERT/UPDATE/DELETE走主库。但要注意,事务块内的SELECT默认还是走主库,因为pgpool无法保证备库数据在事务内一致。如果业务能接受轻微延迟,可以开statement_level_load_balance = on,让事务内SELECT也走备库。
3.4 用psql验证pgpool路由是否生效
配完pgpool,用psql连pgpool端口,执行查询看路由。
# 连接pgpool端口,默认9999 psql -h 192.168.1.20 -p 9999 -U app_user -d app_db在pgpool里执行SHOW pool_nodes;能看到各节点状态。
-- 在pgpool中查看节点状态 SHOW pool_nodes;输出里status列显示up或down,role列显示primary或standby。如果备库显示down,先查健康检查用户能否连上备库。再执行SELECT inet_server_addr();,多执行几次,如果返回的IP在备库和主库之间跳,说明负载均衡生效了。
4. 避坑指南:主从加pgpool最容易翻车的五个地方
4.1 备库延迟突然暴涨,pgpool把读流量全压上去
现象:备库replay_delay从几MB涨到几GB,应用读到的数据越来越旧。原因通常是主库有大事务或批量写入,备库单线程回放跟不上。pgpool默认会把读流量按权重分发,备库越慢积压越多。解决:在pgpool里设backend_weight1 = 0临时把备库读权重降为零,等延迟追平再恢复。长期方案是开Postgres并行回放,max_parallel_apply_workers_per_subscription调大,但只对逻辑复制有效,流复制回放本身是单进程,根本办法是控制主库大事务。
4.2 健康检查用户权限不足导致误判
现象:备库明明活着,pgpool却标记为down,触发切换。原因:health_check_user没有连接权限,或者pg_hba.conf没放行pgpool所在IP。解决:给健康检查用户单独授权,pg_hba.conf里加一条host all health_user 192.168.1.20/32 scram-sha-256。注意健康检查用户不需要超级权限,但要有CONNECT权限。
4.3 failover脚本执行了但pgpool没更新节点角色
现象:主库宕机,备库被提升为新主库,但pgpool仍然把写请求发到旧主库。原因:failover_command脚本只做了pg_ctl promote,没有调用pcp_attach_node通知pgpool。解决:脚本里提升备库后,执行pcp_attach_node -h localhost -p 9898 -U pgpool_admin -n 1把新主库挂回pgpool。同时用pcp_detach_node把旧主库摘掉。这些pcp命令需要pcp.conf里的管理账号密码。
4.4 应用连接串没改,直连了Postgres而不是pgpool
现象:pgpool配好了,但应用还是连的5432端口,高可用完全没生效。原因:应用配置里数据库地址写的是主库IP加5432。解决:把应用连接串改成pgpool的IP和9999端口。如果应用用了连接池(如HikariCP),还要注意连接池自己也有健康检查,可能和pgpool的健康检查冲突,建议把应用侧连接池的connectionTimeout设短一点,让pgpool来管后端。
4.5 同步复制下备库宕机导致主库写入挂起
现象:开了同步复制,备库一挂,主库所有写操作卡住不动。原因:synchronous_standby_names指定的备库失联,主库等待确认超时。解决:临时在主库执行ALTER SYSTEM SET synchronous_standby_names = '';并SELECT pg_reload_conf();降级为异步,恢复写入。长期方案是配两个同步备库,用ANY 1 (standby1, standby2)语法,只要一个确认就行。或者用FIRST 1加优先级,避免单点。
5. 进阶技巧:用pcp命令做手动切换与状态巡检
pgpool自带一套pcp命令,用来在运行时管理节点,比改配置文件重启优雅得多。最常用的三个:pcp_detach_node摘除节点、pcp_attach_node挂回节点、pcp_recovery_node触发节点重建。手动切换主备时,顺序很重要。
# 手动切换:先把旧主库降级为备库 pcp_detach_node -h localhost -p 9898 -U pgpool_admin -n 0 # 提升备库为新主库 pg_ctl promote -D /var/lib/pgsql/data # 把新主库挂回pgpool pcp_attach_node -h localhost -p 9898 -U pgpool_admin -n 1pcp_detach_node的-n参数是节点ID,对应backend_hostname后面的数字。执行这些命令需要pcp.conf里配置的管理用户和密码,默认路径在/etc/pgpool-II/pcp.conf。密码是MD5加密的,用pg_md5生成。
巡检时我习惯写一个脚本,定时跑SHOW pool_nodes和主库的pg_stat_replication,把结果落到日志里。关键指标看三个:备库的replay_delay是否持续增长、pgpool节点状态是否频繁down、切换脚本最近一次执行时间。有一次线上备库磁盘写满,replay_delay涨到几十GB,pgpool健康检查还显示up,因为健康检查只查连接不查延迟。后来在巡检脚本里加了延迟阈值告警,超过500MB就发通知。这个血泪经验告诉我,pgpool的健康检查是黑匣子,不能全信,得自己补监控。
另一个技巧是给pgpool配sr_check_period和sr_check_user,让pgpool自己查流复制状态,而不是只靠TCP连接判断。这样备库延迟过大时pgpool能主动把读流量切回主库。配置如下:
sr_check_period = 10 sr_check_user = 'health_user' sr_check_password = 'health_pass' sr_check_database = 'postgres'sr_check比health_check更贴近真实复制状态,建议两个都开。但注意sr_check_user需要有查询pg_stat_replication的权限,普通用户默认看不到,得授权pg_monitor角色。
最后说一个我自己的习惯:每次改pgpool配置前,先备份pgpool.conf,然后用pgpool -n -d前台调试模式启动,观察日志里节点注册和健康检查的输出,确认没问题再切回后台。这个后悔药救过我好几次,因为pgpool配置写错一个参数,可能启动时不报错,但运行时路由全乱。希望帮到你。
本文还有配套的精品资源,点击获取