先说背景,这套方案是我在维护一套核心业务系统时逐步打磨出来的,当时线上刚好遇到主库磁盘告警导致服务不可用的“事故”,从那次之后就把高可用方案彻底重构了一遍。上一篇文章里已经聊了如何基于流复制搭出一主一备的基础架构,这篇重点放在pgpool-II上,目标很简单:让主库挂了以后应用几乎无感切换,同时把只读查询和写操作的流量分开,让备库也真正干活,而不是当纯摆设。如果你正在做PostgreSQL高可用,或者被读写分离搞得头大,这篇文章应该能省你不少事。
说实话,pgpool-II这个组件放在PostgreSQL生态里属于“老熟人”级别,但用好的真不多。很多文章只讲怎么装、怎么启动,从来没告诉你它内部是怎么检测故障的,failover脚本里到底该写什么东西,为什么切换完了负载均衡不生效。这些坑我一个一个踩过来,今天把能说的全写出来。
1. 为什么在 PostgreSQL 高可用里选 pgpool
先说清楚一个根本问题:PostgreSQL自身的高可用能力其实很强,尤其流复制机制非常成熟,但它在“客户端接入层”是缺位的。你不可能让每个应用自己去感知主备切换、自己改连接地址,那会把人逼疯。pgpool-II解决的就是这个接入层的问题,它充当应用和数据库之间的“交通调度员”。
1.1 它到底解决了什么痛点
- 连接池:应用频繁创建数据库连接的代价很大,pgpool可以在中间做连接复用,几百个并发连接压缩到几十个后端连接,对数据库非常友好。
- 自动故障转移:主库异常时,pgpool通过健康检查机制感知节点故障,自动提升备库为新主库,并且切掉到旧主库的连接,应用端的连接被透明地转移到新主库。
- 读写分离:PostgreSQL自身只能通过第三方插件或者中间层做读写分离,pgpool根据SQL语句类型进行自动路由,SELECT走备库,INSERT/UPDATE/DELETE走主库。
- 负载均衡:在有多个只读节点时,pgpool可以把读请求分发到不同的备库,分散压力。
这四个能力单独拎出来,随便一个都值得用pgpool。四个加在一起,基本上就是中小团队不需要自研代码就能拿到的高可用完整方案。
1.2 对比其他方案,pgpool输不输
我见过有人用HAProxy做读写分离,也有人用Patroni+etcd做自动切换。不能说哪个绝对好,但pgpool有个非常明显的差异化优势:它内置了SQL级别的读写判别逻辑,而不是像HAProxy那样只能做IP和端口级别的转发。你让HAProxy去区分SELECT和UPDATE,它是做不到的,除非你自己搞中间件解析协议。
Patroni很强,强在自动选主和分布式一致,但它偏重配置、依赖etcd等外部组件,运维心智成本高。pgpool-II可以在不引入额外分布式组件的前提下完成主从切换和读写分离,这对很多只有一两套PostgreSQL环境的团队来说,部署成本和理解门槛都低得多。当然,它的watchdog模块也能实现多节点仲裁,后面展开说。
2. 环境准备与整体架构设计
干活之前先把架构图在脑子里画清楚。一个标准的pgpool读写分离+高可用架构通常包含这些角色:
应用客户端 | | 读写分离 + 连接池 + 故障转移 v Pgpool-II节点(建议双节点部署,配合watchdog做守护) | +--------------+--------------+ | | v v 主库(Primary) 备库(Standby) 流复制 流复制2.1 版本兼容性这件事先搞清楚
我踩过最典型的坑是版本匹配。pgpool-II不同版本对PostgreSQL的支持范围是不一样的,直接决定你后面能不能愉快地用它的在线恢复等功能。我当前使用的是pgpool-II 4.4 + PostgreSQL 14,配合良好。如果你用的是PostgreSQL 15/16,最好选择pgpool-II 4.4及以上版本,具体支持矩阵官方文档里有明确说明,动手前先花两分钟确认,别装完以后发现功能缺失再返工。
在生产环境里我建议直接用官方编译好的RPM或者二进制包,而不是从源码编译。前者省事,后者你要处理一堆依赖问题,纯属给自己加戏。但如果你内网环境没有现成的包源,编译装也问题不大,核心依赖是libpq和OpenSSL。
2.2 机器规划
以最常见的双节点pgpool为例,最少三台机器:
| 主机 | 角色 | 说明 |
|---|---|---|
| node1 | Pgpool节点A + 主库 | 可以同机部署,也可以分开 |
| node2 | Pgpool节点B + 备库 | 同上 |
| node3 | 业务应用节点 | 连接pgpool虚拟IP |
生产上我更推荐把pgpool和PostgreSQL分开部署,原因很简单:故障隔离。如果pgpool和主库在同一台机器,主库机器宕机直接连pgpool也一起没了,高可用意义大打折扣。预算不够时同机部署也只能接受,但至少pgpool节点要双份,用watchdog做热备。
2.3 提前规划虚拟IP
pgpool的高可用集群需要对外提供一个漂移IP,也就是虚拟IP(VIP)。应用只连这个IP,发生主从切换时VIP会自动从故障节点漂移到存活节点。这个灵活性很关键:如果应用直连某个pgpool节点,那个节点挂了应用照样连不上,VIP就是为了解决这个问题。
VIP的规划建议与业务网段一致,提前预留好,不要在部署当天临时选,避免改路由权限等尴尬情况。
3. pgpool核心机制:它是怎么实现切换和读写分离的
很多文章上来就贴配置,不解释原理。这就导致出问题时不知道怎么调试。我先用通俗的话拆一下pgpool的内部机制,明白这些以后你再看配置,基本一眼就知道每个参数在干什么。
3.1 健康检查:pgpool怎么知道数据库挂了
pgpool会定期向后端数据库发送心跳探测信号,这个机制叫health check。它有两种最主要的方式:
- 通过TCP连接检测端口是否活
- 通过真实查询(比如SELECT 1)验证数据库可用性
后者比前者更靠谱,因为有些时候端口还在,但数据库已经僵死,比如进程假死、事务挂起耗尽连接数等。这种情况TCP检测发现不了,但查询检测能发现。所以配置中我强烈建议开启查询检测,把health_check_period设置成合适的间隔。
间隔多长时间看你的业务容忍度。设置太短,比如3秒,数据库稍有波动就触发切换,反而影响稳定性;设置太长,故障后应用被迫等待很久。我的个人经验是health_check_period = 10,配合health_check_timeout = 5,整体故障感知时长控制在15秒左右。当然你也可以往下压,但要经过充足测试。
3.2 故障切换的三个阶段
pgpool检测到主库故障后,会触发一个failover流程,这个流程大致分三步:
- 标记节点异常:pgpool把故障节点从可用节点列表里摘除,后续不再把新请求往故障节点上分发。
- 执行脚本:调用你配置的
failover_command,这个脚本由DBA自己编写,通常用于提升备库为新主库。 - 节点角色迁移:pgpool内部重新维护主备角色信息,随后的读写请求按照新的角色分配。
注意一个细节,pgpool本身并不执行“ALTER SYSTEM SET ...”,它只是一个协调者,真正让备库变成主库的动作,靠的是你在脚本里调用pg_ctl promote或通过SELECT pg_promote()完成。这个脚本写不好,切换就会半途而废。
3.3 读写分离是怎么判断的
pgpool的读写分离不是靠监听端口、也不是靠正则匹配,而是做了SQL解析,识别语句类型。Sentinel逻辑大致是:
- SELECT开头,没有
FOR UPDATE、没有写函数,路由到备库。 - INSERT / UPDATE / DELETE / DDL,路由到主库。
- 事务内语句默认全部走主库,除非开启
allow_sql_comments等特殊选项。
这个机制本身很成熟,但有个容易踩的坑:如果在事务中先执行写操作,再执行查询,pgpool会为了保持事务一致性把所有后续语句都发到主库。个别业务觉得这样不够“负载均衡”,但你反过来想,如果查询读到的是备库上还没同步到的旧数据,那才是灾难。所以这个行为不是bug,是保护机制。别想着关掉它,除非你完全能容忍短暂不一致。
4. 核心配置与实操步骤
下面进入正文的重头戏。我把整个配置拆分成三个大块:后端节点注册、故障切换配置、读写分离配置。三块均给出可直接抄走的配置段,并解释每个参数的作用。
4.1 后端节点注册与用户认证
打开pgpool.conf,首先要配置后端节点信息。常见配置项如下:
# 后端节点数量,这里是一主一备两个节点 backend_hostname0 = '192.168.10.101' backend_port0 = 5432 backend_weight0 = 1 backend_data_directory0 = '/var/lib/pgsql/14/data' backend_hostname1 = '192.168.10.102' backend_port1 = 5432 backend_weight1 = 1 backend_data_directory1 = '/var/lib/pgsql/14/data'说明一下backend_data_directoryx这个参数,很多人忽略它。它必须和lsync在线恢复功能配合使用,如果你不打算用pgpool的在线恢复功能,也可以不写。但写上没坏处,因为它还能帮助pgpool在故障切换时更快确定节点状态。
权重参数backend_weightx的意思是:读请求的负载均衡权重。一主一备时两个权重都设为1,读请求各50%。如果你不想让备库承担太多读压力,可以把备库权重调低到0.3,让约23%的读请求到备库。这个参数非常灵活,按业务需求来。
节点注册完成后,还要为pgpool配置数据库用户认证。pgpool需要能连接后端数据库去执行健康检查和故障切换,自然需要用到账户。建议不要用超级用户跑日常连接,专门创建一个具备必要权限的管理账户,比如:
CREATE USER pgpool_user WITH REPLICATION; CREATE USER app_user WITH PASSWORD 'your_password';4.2 主从自动切换的完整配置
故障切换最核心的配置有三项:
health_check_period = 10 health_check_timeout = 5 health_check_user = 'pgpool_user' health_check_password = 'your_password' health_check_database = 'postgres' # 故障切换时执行的脚本 failover_command = '/etc/pgpool/failover.sh %d %H %R'关于failover_command的传参,这是极其重要又容易写错的部分。pgpool在执行脚本时会传入一系列参数,%d是宕机节点的ID,%H是新主库的主机名,%R是新主库的数据目录。你要在脚本里把这些参数接住。
下面是我生产环境里一份验证过很多次的failover脚本,可直接借鉴:
#!/bin/bash # failover.sh # 参数说明: # $1 = failed node id (被标记为故障的节点ID) # $2 = new master host (pgpool决定提升为主库的节点主机名) # $3 = new master data dir (新主库的数据目录) FAILED_NODE_ID="$1" NEW_MASTER_HOST="$2" NEW_MASTER_DATA_DIR="$3" LOG_FILE="/var/log/pgpool/failover.log" echo "$(date '+%Y-%m-%d %H:%M:%S') failover triggered. failed_node=$FAILED_NODE_ID new_master=$NEW_MASTER_HOST dir=$NEW_MASTER_DATA_DIR" >> $LOG_FILE # 在备库上提升其为主库 if [ "${NEW_MASTER_HOST}" != "" ]; then ssh postgres@${NEW_MASTER_HOST} "/usr/pgsql-14/bin/pg_ctl promote -D ${NEW_MASTER_DATA_DIR}" >> $LOG_FILE 2>&1 fi # 注意:这里不是强制要求ssh操作,也可以采用其他方式远程执行 exit 0这段脚本的逻辑不复杂,但有几个细节值得强调。
第一,pg_ctl promote需要在备库所在机器上执行。如果你的pgpool与数据库分离部署,脚本是在pgpool机器上执行的,所以要通过SSH远程过去执行提升操作。SSH免密登录这一步提前配置好,否则切换时脚本卡在密码输入处,整个故障转移就废了。
第二,脚本开头建议写日志这个动作,别省。没有日志你根本不知道failover脚本有没有被触发,有没有报错。先在本地落一条活生生的记录,排查问题时你会感谢自己。
第三,脚本权限。创建完脚本后记得chmod +x,并且把属主设为pgpool运行所用的操作系统用户,否则可能执行不了。
另一个需要配置的是follow_primary_command,它的作用是当新主库产生后,旧备库要重新跟新主库建立流复制。在当前场景(只有一主一备)故障切换时,旧主库宕机,新备库的角色就是空的,不需要再配置。当你有两个以上备库时,你必须配置它,否则其他备库不知道新的复制源是谁,会停在原地等。
4.3 读写分离的配置
读写分离的控制关键在load_balance_mode和master_slave_mode这两个开关:
load_balance_mode = on master_slave_mode = on master_slave_sub_mode = 'stream'master_slave_sub_mode = 'stream'是让pgpool知道后端用的是流复制,它才能正确识别主备。有人会在无意中把这个参数漏掉,导致pgpool仍然用旧的“主从复制”逻辑判断,负载就不对。这三个参数必须同时开启。
还有一个容易被忽略的参数:white_function_list和black_function_list。它俩用来定义“哪些函数是写函数、哪些是读函数”。比如公司业务里普遍用nextval('seq')获取序列值,这个实际上属于写操作,会修改数据库状态。pgpool如果不知道,可能会试着把包含nextval()的查询路由到备库,导致序列值在不同库不一致,甚至报错。解决办法就是把它列入黑名单:
black_function_list = 'nextval,setval,pg_advisory_lock,pg_try_advisory_lock'同样,如果你有一些自定义的只读函数(比如查只读视图的封装函数),可以把它加入white_function_list。一般建议宁可多列黑名单,也不要乱导流。
4.4 watchdog的守护配置
高可用的pgpool需要禁得起单点pgpool故障的考验。这里就轮到watchdog模块上场。watchdog用于监控pgpool自身节点,当某个pgpool节点故障时,另一个接管VIP。配置中需要开启watchdog并定义节点。
use_watchdog = on wd_hostname = '192.168.10.101' wd_port = 9000 wd_interval = 5 # 本节点的其他连接信息 wd_lifecheck_query = 'SELECT 1' wd_lifecheck_dbname = 'postgres' wd_lifecheck_user = 'pgpool_user' # 配置参与竞选的pgpool节点列表 other_pgpool_hostname0 = '192.168.10.102' other_pgpool_port0 = 9999 other_wd_port0 = 9000 # 虚拟IP设置 delegate_IP = '192.168.10.200' ifconfig_path = '/sbin' if_up_cmd = '/usr/sbin/arping -q -U -c 3 192.168.10.200' if_down_cmd = 'ip addr del 192.168.10.200/24 dev eth0'Watchdog的配置比我写的要多不少,但核心逻辑就是上面这些。有一件事必须单独强调:watchdog节点之间通信涉及网络端口和心跳协议,在云平台环境里,组播或广播方式经常受限制,我建议用单播模式,并且提前测试两台机器间的9000端口通信是否顺畅。有次我在内网云主机上卡了半天,最后发现是安全组把9000端口默认拦了,watchdog一直在报脑裂,吓得我差点回退方案。
虚拟IP的if_up_cmd中我加了arping广播,作用很简单:让同一网段的其他机器尽快刷新ARP缓存,把VIP对应的MAC地址切换过来。如果不做这一步,可能某些设备缓存了旧MAC地址,导致即使VIP漂移了,业务包还是发往旧机器,切换等于白做。
4.5 数据库端需要配合调整的参数
不要以为只熊pgpool就完事了,主库和备库自身的配置同样要跟上。
在PostgreSQL主库上,wal_level必须是replica或者更高(PG14默认是replica,因为logical已经被细分出去),max_wal_senders至少设置为2以上,预留一个给流复制,一个给基础备份和在线恢复。max_connections也别乱设,因为pgpool还会保留自己的后端连接,直接相关的是后端的max_connections决定了pgpool能复用多少连接。配合pgpool的连接池,总的会话数自然是受控的。
备库上还有一个容易被忽略的设置:hot_standby = on,开启后备库才能处理只读查询。如果这里没开,pgpool把读请求发过去,备库直接报错,读写分离就成了空谈。
配置全部改完后,先重启PostgreSQL,再重启pgpool,顺序不能反。
5. 实操演示:手动触发主从切换并验证读写分离
配置装好了,必须验证。不验证的高可用就是个玄学,没人敢保证它真能扛住事故。
5.1 验证读写分离是否生效
先用pgpool提供的命令查看后端节点状态:
pcp_node_info -h 127.0.0.1 -p 9898 -U pgpool_user -w正常会显示两个节点,其中一个state为primary,另一个为standby。两个节点都处于up状态代表连接正常。
然后登录到备库,开启查询日志跟踪,比如临时把log_statement = all打开,看某个执行了SELECT的应用,它的连接到底是从主库还是备库出来的。更直观的办法是在PostgreSQL主备两端分别查询当前活跃会话,对比客户端来源IP:
SELECT pid, usename, client_addr, application_name, state FROM pg_stat_activity;我自己的验证方法是:在备库上创建一个一模一样的中间表,往里面插入一行带特殊标记的数据,然后让应用执行SELECT,看能不能查到这条数据。能查到说明读请求到了备库;查不到说明还压在主库上。这个测试虽然“野”,但非常直观。
还要注意一个非常细节的验证点:直接在psql里执行SHOW transaction_isolation,你会发现通过pgpool连接数据库后,看到的client_addr是pgpool所在机器的IP,而不是应用的真实IP。这不是问题,是pgpool的代理特性。必要时可以通过backend_application_name等方式区分来源。
5.2 模拟主库故障,验证自动切换
准备好一个小窗口,保持一个长事务在应用里跑着(模拟正常业务流量),然后找一个维护窗口,手动停掉主库。
# 在主库机器上执行 pg_ctl stop -m fast注意这里我用了-m fast,这是模拟非正常停机,会让活跃事务中断。如果直接shutdown,主库还会发一个优雅关闭的信号给备库,备库不会自己去抢主角色,这样就测不出真正的故障转移逻辑。
停掉主库后,观察pgpool日志。正常情况下你会在几秒内看到类似下面的日志:
LOG: failover: no candidate, then declare failure node LOG: starting failover process...如果日志停在no candidate这一步,说明pgpool没能识别出一个可以提升的备库。这时先不要慌,去检查备库的流复制状态是否正常,尤其确认备库的pg_wal目录有没有在接收新数据。以前我遇到过一次,备库的replication slot坏了,备库实际已经落后主库很多,pgpool检查备库时发现数据不一致,拒绝对它做提升。这种情况下你需要先修复流复制链路,再考虑failover。
当failover正常完成后,再用pcp_node_info查节点状态,备库的角色已经变成primary,旧主库被标记为down。
同时验证一下VIP的漂移地址:
ip addr show在原来的pgpool节点上如果看到VIP消失、在备节点上出现了VIP,说明VIP漂移成功。如果VIP没漂移,八成是watchdog状态没确认,先检查pcp_watchdog_info看看两个节点的状态是不是都在线。
5.3 恢复旧主库并让它重新成为备库
这是很多经验不足的人会漏掉的环节。主库恢复后,通常它已经比新主库的数据落后了一大截。此时绝不能直接把它加回去当主,你要做的是把它降级为备库,然后通过流复制追上最新数据。
操作流程如下:
- 确保旧主库的data目录不变。
- 修改旧主库的
postgresql.conf,加入或确认primary_conninfo指向新主库。 - 删除旧主库data目录下的
standby.signal,让PostgreSQL以standby模式启动(PostgreSQL 12+的机制)。 - 启动旧主库,确认它变成新主库的备库。
具体命令大致是:
# 在旧主库机器上 rm -f $PGDATA/standby.signal cat > $PGDATA/postgresql.conf.append <<EOF primary_conninfo = 'host=192.168.10.102 port=5432 user=replicator application_name=standby1' EOF pg_ctl start启动后观察新主库的pg_stat_replication,看到旧主库的进程在线且state=streaming,说明恢复成功。然后也在pgpool里把节点重新加回来:
pcp_attach_node -h 127.0.0.1 -p 9898 -U pgpool_user -n 0把节点重新挂载后,pgpool会把它作为备库纳入读写分离池。这一步一定要等流复制同步追平后做,不然这个节点会拖累整体负载均衡的可用性。
6. 常见问题与排查技巧实录
问题1:failover半天不触发,日志里也没动静
处理思路:先确认health_check_period和health_check_timeout配得是否太保守。如果主库进程彻底僵死但端口还开着,health_check_timeout太短可能导致检测误判;如果health_check_period太长,故障感知自然慢。我实践中还遇到过一种隐蔽情况:pgpool的health_check_user密码过期或被误改,导致健康检查一直失败但pgpool日志里错误级别不高,你没注意到。排查这个问题的第一手段就是把pgpool日志级别调到LOG或DEBUG1,然后手动SELECT pg_terminate_backend(pg_backend_pid())制造一次断连,观察日志反应。
问题2:切换成功了,但应用连接还是一直报错
你确认pgpool日志里已经执行了failover,新主也已经提升成功,但应用还在报“connection refused”。这种情况大概率是因为应用侧连接池还保留着旧连接,没有感知到数据库端连接被断开。pgpool在failover后应该会主动断开到故障节点的连接,但如果应用侧用的是长连接且没有设置合理的连接存活检测,一直复用旧连接自然会失败。
解决办法有两步:应用端连接池配置里开启连接有效性检测,比如HikariCP的connection-test-query和validation-timeout;pgpool侧把client_idle_limit设一个合理值,长时间空闲的连接自动断开,倒逼应用新建连接。这个组合拳能有效缓解切换后的“僵尸连接”问题。
问题3:读流量没到备库去
这是新手最容易遇到的现象:配置用了读写分离,但备库的查询日志一点动静没有。检查顺序如下:
load_balance_mode和master_slave_mode是否都为on。master_slave_sub_mode是否是stream。- 查看
pcp_node_info里两个节点的weight是不是都不为0。 - 检查是否有显式在这个会话里发过写语句,导致会话级别强制走主库。
前三个没问题的话,十有八九是会话级行为。很多应用框架会默认在同一个连接里使用事务,而事务内读请求默认全部主库,这是最最常见的“读写分离不生效”的原因。如果你用的是ORM框架(比如Hibernate),又开启了事务,所有的SELECT都会走主库,从备库日志上看起来就像完全没有负载均衡。处理方式:要么把只读查询单独走只读连接,要么在pgpool里开启allow_sql_comments来强制某些语句路由到备库(但这是高级玩法,不推荐无脑用)。
问题4:watchdog脑裂
所谓脑裂是指两个pgpool节点同时认为自己才该持有VIP,导致网络风暴或者业务连接混乱。表现是VIP在两个节点间跳来跳去,应用连接频繁中断。脑裂在云环境里尤其容易发生,因为云网络对ARP、组播的支持与物理机不同。
排查方法:看watchdog日志,确认两个节点互相能ping通、9000端口能通;检查wd_lifecheck的配置,确认wd_hostname写的是本机实际IP,而不是127.0.0.1。事实上很多脑裂就是wd_hostname写错导致节点间互相误判对方失联。出现脑裂后,先人工指定一台为权威节点,把另一个节点的watchdog停掉,再逐步恢复。
问题5:切换后新主库没有开启只读处理能力
有时候你按脚本执行了pg_ctl promote,备库变成了主库,但查看pg_is_in_recovery(),发现还是t。这是因为pg_ctl promote执行时,如果备库还没有完全拿到主库的WAL日志,它其实在等待日志补齐,这个过程可能需要一点时间。别急,可以在业务维护窗口多等几秒,再验证pg_is_in_recovery()返回f。如果一直不返回,检查主备之间的WAL传输是否有断档,或者磁盘空间是不是满了。
问题6:故障切换后数据丢失
说句扎心的话,任何流复制方案都有极小概率的数据丢失窗口。因为流复制是异步的,主库崩溃时,最后产生的WAL日志可能还没来得及传给备库。pgpool本身不具备数据补齐功能,它只能把备库顶上。如果你业务零丢失是刚需,那得上同步复制sync replication,但这会降低性能,需要权衡。
我的做法是从业务层做兜底:核心写入操作在应用里双写一份到消息队列,数据库恢复后再做对账。听起来老土,但真发生切换时我睡得着觉。你也可以考虑在切换前用pg_wal里的部分日志做归档恢复,把数据捞回来。总之,别天真地以为pgpool能帮你把数据丢失变成零。
7. 一些后期运维建议
这一章不算严格技术点,但却是我在若干次真实事故里换回来的心得。
7.1 慢SQL的追踪
用pgpool后,你要特别小心“读请求全到主库”导致的慢SQL问题。开启读写分离时,一定在PostgreSQL配置里打开log_min_duration_statement,建议设1000ms,超过1秒的SQL记日志。这样一旦备库没有分担流量,你通过日志能直观发现有大量慢查询在主库执行。
7.2 备库延迟监控
备库延迟是读写分离的心腹大患。备库落后太多,读请求虽然发过去了,返回的数据却是“昨天”的。对很多业务来说这不可接受。我每天都会定时跑一次监控SQL,检查主备延迟:
SELECT pid, application_name, state, pg_wal_lsn_diff(pg_current_wal_lsn(), replay_lsn) AS lag_bytes FROM pg_stat_replication;如果lag_bytes持续增大,就要考虑是不是备库IO能力不足、网络带宽受限或者归档任务拖慢了备库刷盘速度。不要等到用户反馈数据不对才去查。
7.3 定期演练故障切换
这点真的重要。我见过太多团队搭好了高可用,一年都不切一次,结果真出事时,脚本执行权限错了、新主库起不来、VIP漂不过去,被发现时全已晚。
我的建议是每个月做一次模拟切换演练,时间挑在业务低峰期,切完再切回来。把每次切换的时间、日志、报错记录到运维文档里,形成自己的切换预案。演练中发现的问题才值钱,生产事故里暴露的问题代价太大。
7.4 再看一眼备份
最后这个提醒非常容易被忽略:pgpool只是高可用,不等于备份。如果说故障切换是“让人继续上班”,那备份就是“万一这个员工彻底失联,你还有档案”。流复制并不能替代定时备份。即使有了pgpool,全量备份+归档WAL的备份任务仍然必须跑,不能省。我有一个血的教训:高可用方案上线后,团队高兴了半年,结果一次误删数据事件,由于没有独立备份,恢复起来极其痛苦。从那以后我一直保留独立的备份集,每天验证备份可恢复性。
好了,pgpool做PostgreSQL高可用、主从切换和读写分离的核心细节基本都在这里了。个人最大的体会是:架构设计时别光盯着功能,还要想清楚故障转移的全链路——从健康检查到节点提升,再到应用连接重连,每一环都要能观测、可验证。踩过几次坑之后,我对自己这套方案最大的信心来源不是配置写得多漂亮,而是每一次演练都实实在在地做过。希望这篇文章能让你少走弯路,如果有其他问题,欢迎顺着实操步骤去验证,测试环境多折腾几次比什么都强。