简介:《Postgres主从流复制+pgpool高可用方案》是一份面向PostgreSQL运维、数据库架构与高可用实践者的技术文档,聚焦基于WAL日志实时传输的主备复制,以及借助pgpool-II实现连接池、故障切换、负载均衡与读写分离的落地方案。压缩包共1个docx文件,约773KB,内容按方案介绍、实践环境、客户环境准备、资源调整、主备与pgpool配置、启动测试等章节组织,便于按步骤查阅。文档对同步/异步复制取舍、主库参数设置、备库初始化、pgpool健康监控与切换策略、pg_rman备份恢复等关键环节展开说明,并给出故障模拟测试思路,适合用于搭建实验环境、部署参考和排错对照。已有471人学习下载,可作为中高级数据库运维人员构建高可用PostgreSQL集群的实用资料。
1. 主库宕机 30 分钟的代价:为什么单机 PostgreSQL 撑不住核心业务
一台跑 PostgreSQL 的 Linux 服务器硬盘控制器坏掉,从监控告警到业务方打电话过来只隔了七分钟,把备份恢复回去却花了三个多小时。这套 Postgres 主从流复制 + pgpool 高可用方案要解决的就是这件事:主库挂掉时有人能接管,只读查询有地方分流,误删数据之后还有一份能回到任意时刻的备份。它适合手里有两三台 Linux 服务器、跑着中等规模业务库、需要自己把数据库高可用搭起来的开发和运维人员;也适合把数据库高可用当作毕设或课程设计题目的人。这套方案从需求、架构、参数配置到故障演练是完整的,照着走一遍就能拿出一份有组网图、有配置清单、有切换测试记录的交付物,而不是一句「用主从复制就行」的含糊结论。
2. 总体架构设计与选型:流复制、pgpool-II 与 pg_rman 的三层分工
先把三个组件的边界划清楚,后面所有配置才不会互相打架。这一章不涉及具体命令,只解决「为什么这么搭」以及「哪些参数必须一起改」的问题,理解偏差通常就出在这里。
2.1 为什么用流复制而不是 WAL 归档传送
PostgreSQL 把每一次数据页变更都写进pg_wal目录下的 WAL 日志,这份日志本身既可以用来做崩溃恢复,也可以用来在另一台机器上重放。把 WAL 送到备库有两条路:一条是归档传送,配置archive_command把写满的 WAL 段文件拷贝到备机目录,备机用restore_command拉回来重放;另一条就是流复制,主库的 walsender 进程通过 TCP 长连接,把 WAL 记录实时推给备库的 walreceiver。
归档方式的短板很直接:WAL 段写满才会传送,单个段默认 16MB,业务量小的时候可能十几分钟都写不满一段,备库就永远落后一截。这个落后量在故障切换时就是真实的数据损失。流复制的延迟通常在毫秒到秒级,主库提交后 WAL 记录马上被推走,备库的 startup 进程持续重放,备机上的数据始终接近主库。归档方式不是没用,它的价值在 PITR——误删表、错误更新这类逻辑故障,靠的是把全量备份加上归档日志重放到误操作之前的某个时间点,这一块由 pg_rman 承担,和流复制是两套并行的机制,谁也替代不了谁。
2.2 三个组件的职责边界与数据流向
客户端连接的是 pgpool 暴露的虚拟 IP,pgpool 往后端节点转发 SQL。写请求落到 node0(当前主库),读请求按backend_weight分发到两个节点。主备之间的数据同步完全不经过 pgpool,是 PostgreSQL 自己的 walsender 和 walreceiver 在干活。
| 组件 | 承担的职责 | 刻意不承担的职责 |
|---|---|---|
| PostgreSQL 流复制 | 主备实时数据同步、备库只读分流 | 连接管理、故障探测、流量分发 |
| pgpool-II | 连接池、读写分离、健康检查、failover 触发、VIP 漂移 | 不参与 WAL 传输,不负责数据复制 |
| pg_rman | 全量/增量备份、归档日志管理、按时间点恢复 | 不参与在线切换流程 |
很多人看到 pgpool-II 文档里写着「复制模式」就想顺手打开,那样反而会出事。在原生流复制已经生效的前提下,pgpool 的复制功能属于重复劳动,一旦两条链路的状态不一致,排查成本会翻倍。这套方案的立场很明确:数据同步只信 PostgreSQL 自己的流复制,pgpool 只做路由和切换,pg_rman 只管备份。
2.3 同步复制的代价:synchronous_commit 到底怎么设
异步复制下,主库提交事务不需要等备库确认,性能好,但主库崩溃时可能丢掉最后几百毫秒的事务。同步复制要求主库等到备库也写完 WAL 才返回提交成功,synchronous_commit提供了一档一档的精细度,不用在「全同步」和「全异步」之间二选一。
# master: postgresql.conf 中与复制相关的核心参数 wal_level = replica # 至少要 replica,logical 用于逻辑解码,开销更大 max_wal_senders = 10 # 允许的 walsender 上限,按备库数量留余量 wal_keep_segments = 512 # 保留 512 个历史 WAL 段,约 8GB,防止备库断连后追不上(PG13 起改名为 wal_keep_size) synchronous_standby_names = 'standby1' # 非空即代表开启同步复制,名字要和备库的 application_name 对齐 synchronous_commit = remote_write # 主库 WAL 落盘 + 备库收到并写入操作系统缓存才返回成功synchronous_commit的五个取值里,off和local实际还是异步语义;on要求备库写完 WAL 文件;remote_write只要求备库把 WAL 收到并写到操作系统缓冲区,不等 fsync;remote_apply则要求备库已经把日志重放完,读请求在备库上一定能看到最新数据,代价也最大。我一般的做法是核心交易类业务用remote_write,报表类、日志类业务直接异步。synchronous_standby_names一旦配上,主库的写延迟就取决于主备之间的网络质量——两台机器跨机房的时候,这个参数能把事务响应时间从 2ms 拉到 20ms,上线前必须用真实业务压一遍再定。
2.4 双节点还是三节点:VIP 与 watchdog 的取舍
最省资源的搭法是两台机器,PostgreSQL 和 pgpool 都装在各自机器上,node0 主库加 node1 备库,pgpool 的 watchdog 组件负责互相监控,delegate_ip让虚拟 IP 在两者之间漂移。客户端连 VIP,永远不知道背后是哪台机器。
双节点的问题在于「互相监控」这个模式本身:两台机器的网络一旦出现单向不通,两边都可能认为对方挂了,于是同时把自己提升成主库,这就是脑裂。要压住它,watchdog 的心跳最好走单独一块网卡或者独立交换机,wd_priority明确区分优先级,让优先级高的节点在争议时更倾向于保留 VIP。当节点数量超过两台,或者对切换时间有硬性要求(比如 30 秒内必须完成),通常的做法是单独部署一台 pgpool 或者部署两套 pgpool 做冗余。本方案以双节点为主线,三节点是在这个骨架上加节点,配置逻辑不变。
3. 环境与流复制落地:从内核参数到 pg_basebackup 的一份可执行清单
这一章是从裸机到「主备已经同步」的完整过程,中间任何一步跳过,后面 pgpool 的健康检查都会以各种奇怪的方式报错。我习惯把它拆成五段:定版本、调系统、配主库、拉备库、验状态。
3.1 版本矩阵与目录规划
版本必须成对考虑,PostgreSQL 大版本变了,流复制的备库标识方式跟着变;pgpool 版本变了,watchdog 和 failover 的参数名也会变。
| 组件 | 旧环境版本 | 新环境版本 | 部署位置 |
|---|---|---|---|
| 操作系统 | CentOS 6.5 | CentOS 7.5 | node0 / node1 |
| PostgreSQL | 10.3 | 12.2 | node0 / node1 |
| pgpool-II | 3.6.14 | 4.1.1 | node0 / node1 |
| pg_rman | 1.3.x | 1.3.x | node0(备库可选) |
目录规划先定死,后面所有配置文件的路径都从这里推导:软件装在/opt/pgsql和/opt/pgpool,数据目录/data/pgdata,WAL 归档目录/data/pg_archive,备份目录/data/pgrman_backup,日志目录/var/log/pgpool。数据目录和归档目录建议分盘,归档写满把数据盘撑爆是很典型的翻车场景。
3.2 系统层准备:用户、防火墙、内核参数与时钟同步
先处理四件容易被忽略的事:创建postgres用户并统一 UID、关闭防火墙和 SELinux、配置 hosts 双向解析、主备时钟同步。时钟不同步会让 WAL 时间戳和日志对不上,排查问题时几乎无法还原时间线。
# /etc/sysctl.conf 追加,按物理内存比例调整,下面以 32GB 内存为例 kernel.shmmax = 17179869184 # 单块共享内存上限,一般取物理内存的一半 kernel.shmall = 4194304 # 共享内存总页数 = shmmax / 4096 vm.swappiness = 10 # 降低换出倾向,避免数据库缓冲区被换到磁盘 vm.dirty_background_ratio = 5 # 后台刷脏页的触发比例 vm.dirty_ratio = 10 # 前台刷脏页的强制比例,过大会造成写抖动 net.ipv4.tcp_keepalive_time = 600 # 复制连接的空闲探测,配合 wal_sender_timeout 使用 kernel.sem = 5010 641280 5010 128 # 信号量,并行查询多的时候需要kernel.shmmax设小了,postgresql.conf里shared_buffers调大后实例直接起不来,报的是共享内存申请失败,这个错在日志里只出现一行,很容易被当成权限问题。vm.swappiness调到 10 以下是为了让数据库的共享缓冲区老老实实待在内存里,代价是系统其他进程可能更快被换出,机器如果还跑着别的服务要权衡。紧接着改limits.conf,把postgres用户的nofile提到 102400,否则连接数一多就会遇到too many open files:
# /etc/security/limits.conf postgres soft nofile 102400 postgres hard nofile 102400 postgres soft nproc 16384 postgres hard nproc 16384 postgres soft memlock unlimited postgres hard memlock unlimited时间同步用 chrony,两台机器指向同一个内网时间源,chronyc sources -v能看到同步状态和偏移量。注意别在两台机器上各配一个外部时间源,主备之间的时钟偏差会让pg_stat_replication里的时间字段失去参考价值。
3.3 主库配置:postgresql.conf、pg_hba.conf 与复制用户
主库这边要改三处:监听与复制参数、访问控制、复制账号。参数改完必须重启实例,wal_level是 postmaster 级别的参数,pg_ctl reload不生效。
# node0: /data/pgdata/postgresql.conf listen_addresses = '*' # 至少绑定内网地址,别只留 localhost port = 5432 wal_level = replica max_wal_senders = 10 wal_keep_segments = 512 hot_standby = on # 备库要接受只读查询必须打开 archive_mode = on # pg_rman 做 PITR 的前提 archive_command = 'test ! -f /data/pg_archive/%f && cp %p /data/pg_archive/%f'archive_command里那个test ! -f是防止归档目录已有同名文件时覆盖,返回非零退出码会让 PostgreSQL 反复重试同一个 WAL 段,pg_wal目录就会持续膨胀,这是最容易被忽略的一条。访问控制文件里至少要放行复制连接和应用连接:
# node0: /data/pgdata/pg_hba.conf # TYPE DATABASE USER ADDRESS METHOD host replication repl 192.168.10.0/24 md5 host all app_user 192.168.10.0/24 md5 host all all 192.168.10.0/24 md5复制账号单独建,不要拿超级用户凑合,这样备库连接的权限范围是可控的:
-- 在主库执行,repl 只用于流复制 CREATE ROLE repl WITH REPLICATION LOGIN PASSWORD 'Repl_2024#pg'; -- pgpool 的健康检查账号,只需要能读 pg_stat_replication CREATE ROLE pgpool_check WITH LOGIN PASSWORD 'Chk_2024#pg'; GRANT pg_monitor TO pgpool_check;3.4 备库初始化:pg_basebackup 与 12.x 的 standby.signal 差异
备库不需要手工initdb,直接用pg_basebackup从主库拉一份物理备份,这是最不容易出错的方式:
# node1:以 postgres 用户执行,-R 是关键参数 pg_basebackup \ -h 192.168.10.11 -p 5432 \ -U repl \ -D /data/pgdata \ -Fp -Xs -P -R-Fp表示明文格式而不是 tar 包,方便后续直接改配置文件;-Xs表示备份过程中同时通过流复制拉取 WAL,避免备份期间 WAL 被回收导致备份不可用;-P打进度;-R最省事——它会自动在数据目录下生成standby.signal文件,并把primary_conninfo写进postgresql.conf,省掉手工拼连接串的步骤。12 版本前后的差异就在这里:
# PostgreSQL 10.x(12 之前):需要手写 recovery.conf cat > /data/pgdata/recovery.conf <<'EOF' standby_mode = 'on' primary_conninfo = 'host=192.168.10.11 port=5432 user=repl password=Repl_2024#pg application_name=standby1' recovery_target_timeline = 'latest' EOF# PostgreSQL 12.x 及以后:recovery.conf 已被移除,改用信号文件 touch /data/pgdata/standby.signal # primary_conninfo 写在 postgresql.conf 末尾,application_name 必须与主库 synchronous_standby_names 一致 echo "primary_conninfo = 'host=192.168.10.11 port=5432 user=repl password=Repl_2024#pg application_name=standby1'" >> /data/pgdata/postgresql.conf这一步是整套方案里版本差异最大、也最容易出错的地方。把 10.x 的recovery.conf直接搬到 12 上,实例启动后会忽略它并把自己当成主库,从库变成第二个主库,数据直接分叉。
3.5 复制状态检查与切换动作
备库启动后,先在主库看复制链路是否建立:
-- 主库执行,sync_state 为 sync 才说明同步复制真正生效 SELECT client_addr, application_name, state, sync_state, sent_lsn, replay_lsn FROM pg_stat_replication;# 备库执行,能看到 walreceiver 和 startup 两个进程才算正常 ps -ef | grep postgres | grep -E "walreceiver|startup"state显示streaming表示流复制正常;如果只看到startup没有walreceiver,八成是primary_conninfo里的密码或pg_hba.conf有问题。确认无误后做一次手工切换练手:主库执行pg_ctl promote -D /data/pgdata,备库会从恢复状态转成可写主库,pg_stat_replication变空,pg_is_in_recovery()返回false。这个动作在实际故障中由 pgpool 的failover_command自动触发,但必须手工先跑通一次,否则自动化脚本出错时无从判断是脚本问题还是环境问题。
4. pgpool-II 配置与故障切换:连接池、读写分离与 VIP 漂移
pgpool 这一层的配置项多,但真正决定行为的只有四组:后端节点定义、健康检查、负载均衡、watchdog。搞清楚每一组管什么,剩下的几百个参数按默认值走就行。
4.1 pgpool.conf 的关键参数分组
后端节点定义决定了 pgpool 眼里有几台数据库、谁是主:
# /opt/pgpool/etc/pgpool.conf(node0 与 node1 内容基本一致,仅 hostname 不同) backend_hostname0 = '192.168.10.11' # 初始主库 backend_port0 = 5432 backend_weight0 = 0 # 主库权重设 0,读请求不往主库分,规避主备延迟导致的不一致 backend_flag0 = 'ALWAYS_PRIMARY' backend_hostname1 = '192.168.10.12' # 初始备库 backend_port1 = 5432 backend_weight1 = 1 backend_flag1 = '' enable_pool_hba = on load_balance_mode = on # 读写分离总开关,写走 node0,读按权重分发 connection_cache = on # 连接池,复用已建立的物理连接 num_init_children = 100 # 预建子进程数,即最大并发客户端连接上限 max_pool = 4 # 每个子进程对每个后端节点的缓存连接数num_init_children和max_pool相乘就是 pgpool 向后端能打开的最大连接数,100 × 4 × 2 = 800,主库的max_connections必须大于这个值,否则连接池建立时会报too many connections。这是配置连接池时最常见的算术错误。
健康检查和流复制状态检查是自动切换的依据,两者机制不同:健康检查只判断数据库端口和简单查询是否可用;流复制检查会去查pg_stat_replication,判断主备角色是否正常。
# ---- 流复制状态检查 ---- sr_check_period = 10 sr_check_user = 'pgpool_check' sr_check_password = 'Chk_2024#pg' sr_check_database = 'postgres' # ---- 存活健康检查 ---- health_check_period = 10 # 检查周期,秒 health_check_timeout = 20 # 单次超时,要比网络抖动时间长 health_check_user = 'pgpool_check' health_check_password = 'Chk_2024#pg' health_check_database = 'postgres' health_check_max_retries = 3 # 连续失败 3 次才判定宕机,避免网络毛刺误切health_check_max_retries这个参数不要图快设成 1,一次网络抖动就触发切换,业务方会看到一次毫无必要的抖动。反过来设得太大,真实宕机后要等一分钟以上才切,也不好。三到五次是比较常见的取值。
4.2 pool_hba.conf、pool_passwd 与 pcp.conf 的配合
pgpool 有自己的访问控制,客户端连的是 pgpool 不是数据库,所以pg_hba.conf管不到它,必须再配一份pool_hba.conf,格式和 PostgreSQL 那份一模一样,但用户认证走的是另一个文件:
# 生成 pool_passwd 中的条目,pgpool 用它代替客户端向数据库认证 pg_md5 --md5auth -u app_user -p # 生成 pcp 管理用户,pcp_detach_node / pcp_promote_node 等命令要用它认证 pcp_passwd=...pcp.conf里存的是 pcp 管理员的口令,failover_command调用的pcp_detach_node要用到这个账号。三者关系一旦搞混,症状就是「数据库能连、pgpool 连不上」或者「pgpool 能连、管理命令认证失败」。我一般的做法是把这三份文件的生成过程写进部署脚本,而不是手工敲,手工改密码后漏改其中一份的坑太多。
4.3 failover 触发链路与 VIP 漂移的验证
故障切换的完整链路是:健康检查连续失败 → pgpool 判定节点下线 → 执行failover_command→ 脚本里把备库 promote 成主库 → pgpool 重新加载后端定义 → watchdog 把 VIP 漂到新节点。中间任何一环断了,切换都不完整。
# pgpool.conf 中指定切换脚本 failover_command = '/opt/pgpool/bin/failover.sh %d %h %p %D %m %H %M %P'#!/bin/bash # /opt/pgpool/bin/failover.sh —— 由 pgpool 在检测到后端节点故障时调用 # 参数顺序对应:%d 失效节点编号 %h 失效主机 %p 失效端口 %D 数据目录 %m 新主节点编号 %H 新主主机 %M 新主数据目录 %P 新主端口 FAILED_NODE="$1" FAILED_HOST="$2" FAILED_PORT="$3" NEW_MASTER_NODE="$5" NEW_MASTER_HOST="$6" NEW_MASTER_PORT="$8" # 1) 提升原备库为主库,12.x 用 pg_ctl promote,12 之前的低版本也可以创建 trigger_file 触发 ssh -T postgres@"${NEW_MASTER_HOST}" "/opt/pgsql/bin/pg_ctl promote -D /data/pgdata" # 2) 等待提升完成,最多等 30 秒,超时就记严重日志,交给人来处理 for i in $(seq 1 30); do STATUS=$(ssh -T postgres@"${NEW_MASTER_HOST}" "/opt/pgsql/bin/psql -tAc 'SELECT pg_is_in_recovery()'") [ "${STATUS}" = "f" ] && break sleep 1 done # 3) 留痕,事后要能说清楚是哪个节点、什么时间、切到了哪里 echo "$(date '+%F %T') node${FAILED_NODE}(${FAILED_HOST}:${FAILED_PORT}) failed, promoted node${NEW_MASTER_NODE}(${NEW_MASTER_HOST}:${NEW_MASTER_PORT})" \ >> /var/log/pgpool/failover.logwatchdog 负责 VIP,4.x 的写法是给每个 pgpool 节点编号定义:
use_watchdog = on hostname0 = 'pgpool-a' # 本机 pgpool 标识 pgpool_port0 = 9999 wd_hostname0 = '192.168.10.11' # 本机 watchdog 监听地址 wd_port0 = 9000 wd_priority = 1 # 数值大者优先持有 VIP,双节点必须设置不同的值 delegate_ip = '192.168.10.100' # 客户端连接的业务 VIP ifconfig_cmd = '/sbin/ifconfig' # 心跳走独立网段,避免和业务流量抢带宽导致误判 heartbeat_destination0 = '192.168.20.12' heartbeat_destination_port0 = 9694 heartbeat_device0 = 'eth1'验证 VIP 是否真的漂移,不能只看 pgpool 日志,要在停掉主节点 pgpool 之后,在备机上执行ip addr show确认192.168.10.100已经挂到网卡上,再用客户端连 VIP 跑一条查询。这一步做完,才算这套高可用方案真的立住了。
4.4 pg_rman 备份与归档:把后悔药留好
流复制解决的是机器坏掉,pg_rman 解决的是数据被改坏。归档必须先在数据库里打开,前面postgresql.conf里的archive_mode和archive_command就是为它准备的。
# 初始化备份目录,只需要执行一次 export BACKUP_PATH=/data/pgrman_backup pg_rman init -B "${BACKUP_PATH}" # 全量备份,--with-serverlog 会一并备份数据库日志,排障时很有用 pg_rman backup --backup-mode=full --with-serverlog -B "${BACKUP_PATH}" # 每次备份后必须 validate,否则这份备份在恢复时会被判定为不可用 pg_rman validate -B "${BACKUP_PATH}"日常用两个脚本就够了:pg_rman_full.sh每周做一次全量,pg_rman_archive.sh每小时做一次增量并清理超过保留期的旧备份。恢复演练至少要真做一次——把备份恢复到一个临时目录、改端口启动、查一张业务表,确认数据到点。没演练过的备份等于没有备份,这句话在真出事的时候特别刺耳。
5. 避坑与排查:五类高发故障的现象、原因与解决
前面四章是正常路径,这一章是血泪经验。这五类问题在这套组合里出现频率最高,每一条都值得在部署阶段提前规避。
5.1 备库启动后变成第二个主库
现象:备库pg_ctl start成功,但主库的pg_stat_replication里看不到任何记录,备库本地可以正常写入,两边数据开始分叉。原因基本只有一个——备库的恢复标识没生效。12 之前是recovery.conf文件名或权限不对(必须属于postgres用户且权限 0600),12 之后是standby.signal没创建,或者从旧版本迁移时把recovery.conf原样拷了过来。解决方式是停库检查数据目录里到底有没有standby.signal,postgresql.conf里primary_conninfo是否真的写进去了,权限是否对。启动后用SELECT pg_is_in_recovery()确认,返回t才算备库身份成立。
5.2 no pg_hba.conf entry for host
现象:pgpool 启动正常,客户端连 VIP 时报no pg_hba.conf entry for host "192.168.10.11"。原因有两种可能:一是数据库侧的pg_hba.conf没放行 pgpool 所在网段;二是客户端连的是 pgpool,pgpool 做了第二次连接,pool_hba.conf里没有对应条目,报错信息看起来一样但来源不同。排查顺序是先看报错发生在哪个进程的日志里——数据库日志里有就是第一类,pgpool 日志里有就是第二类。解决方式是两份 hba 文件都要覆盖 pgpool 节点和客户端网段,改完pg_ctl reload和pgpool reload各执行一次。
5.3 all backend nodes are down
现象:pgpool 启动后所有连接都失败,日志刷all backend nodes are down,但数据库本身手工能连。原因通常是健康检查账号不对或者权限不够:health_check_user密码错、该用户在pg_hba.conf里没被放行、或者账号被设成了nologin。还有一种隐蔽情况是health_check_database指向了一个不存在的库。解决方式是拿健康检查账号手工执行一次psql -h 后端地址 -U pgpool_check -d postgres -c 'select 1',能在两台机器上都成功,pgpool 才有资格做判断。这个账号不需要业务权限,给pg_monitor足够。
5.4 VIP 同时出现在两台机器上
现象:故障切换之后,客户端时而能连时而超时,上两台机器查ip addr,发现虚拟 IP 同时挂在两边网卡上。这就是脑裂。原因是 watchdog 心跳网络不通或者延迟过大,两个节点互相认为对方已死,各自把 VIP 绑了上来。解决分两步:短期是手工在优先级低的节点上执行pcp_detach_node或者直接摘掉 VIP;长期是把心跳改到独立网段、独立交换机,wd_priority设置成明确的不同值,并把heartbeat_destination指向对方的独立网卡地址而不是业务地址。业务流量把心跳网卡打满导致的脑裂,在小带宽机器上很常见。
5.5 归档失败把数据盘写满
现象:数据库突然只读,pg_wal目录下堆积大量 WAL 文件,磁盘使用率 100%。原因是archive_command返回了非零退出码——可能是归档目录权限不对、磁盘满、或者test ! -f那个判断拦住了重名文件。PostgreSQL 会持续重试同一个段,新的 WAL 又不断产生,堆积速度取决于写入量。解决方式是在postgresql.conf里配置archive_command时先手工验证命令能成功返回 0,并用监控盯住pg_wal目录大小和pg_stat_archiver里的failed_count。真发生了,先把归档目录腾出空间、修好命令,堆积的 WAL 会自动被归档掉;急着恢复写入的话可以临时archive_mode = off重启,但那段时间的 PITR 能力就断了,要记进变更记录。
6. 验收与答辩:用一张清单证明这套方案真的能切
方案写完不等于能用,能证明它「坏了一台也照常跑」才算交付。我习惯用下面这张清单过一遍,每一项都要有截图或日志作为证据,答辩时直接按顺序演示。
| 检查点 | 验证方式 | 通过标准 |
|---|---|---|
| 流复制链路 | 主库查pg_stat_replication | state=streaming,sync_state=sync |
| 数据一致性 | 主库建表插入,备库查询 | 备库立即可见(延迟小于 1 秒) |
| 只读分流 | 连 VIP 执行多条SELECT | pgpool 日志显示分发到 node1 |
| 主库宕机切换 | kill掉主库 postmaster 进程 | 30 秒内 VIP 漂移,写入恢复 |
| 复制中断恢复 | 停备库 10 分钟再启动 | 自动追平,无需重建备库 |
| 备份可用性 | pg_rman validate加一次真实恢复 | 恢复到指定时间点,数据完整 |
| 连接池上限 | 压测到num_init_children | 排队而非直接报错 |
| 脑裂防护 | 拔掉心跳网线 | 只有一个节点持 VIP |
演示顺序建议从「正常态」开始:先展示 VIP 在主节点、pg_stat_replication为 streaming、备库只读查询正常,让评审看到系统的基线状态。然后做「故障态」:直接kill -9主库的 postmaster,盯着 pgpool 日志和 VIP,30 秒内说明切换完成,再从客户端连 VIP 写一条数据、在 node1 上查出来。最后做「恢复态」:把原主库拉起,让它以备库身份加入,展示pg_basebackup重建或者直接追平的过程。三步走完,这套方案的说服力比任何文字描述都强。
我第一次做这套演练的时候,切换后忘了验证pool_passwd里的老密码,客户端一直报认证失败,白白在答辩现场耗了十分钟。从那以后每次演练前,我都强制把「客户端用业务账号连 VIP 跑一条增删改查」写进检查表第一步,而不是只盯着进程和 VIP。希望这套结构和清单能帮到你,照着走一遍,你也能交出一份经得起现场演示的数据库高可用方案。
本文还有配套的精品资源,点击获取