简介:这份资源是一份面向 PostgreSQL 运维与架构人员的《Postgres主从流复制+pgpool高可用方案》技术文档,聚焦数据库主备架构的搭建与高可用保障。内容围绕流复制协议展开,讲解 WAL 日志如何由 walsender 实时推送至备库 walreceiver,并涵盖同步与异步两种复制模式的取舍、pgpool-II 连接池管理、故障自动切换、负载均衡与读写分离、超限连接限制以及 pg_rman 备份恢复等要点,同时给出软件环境规划、环境示意图、用户与权限配置、主库与备库参数调整、pgpool-II 策略设置及启动后故障模拟验证的完整流程,适合需要落地生产级高可用方案的 DBA 或后端开发者参考。资源包仅含 1 个 docx 文档,约 773KB,目录按方案介绍、实践环境、客户环境准备、资源调整等章节组织,便于按步骤对照实施。目前已有 471 人学习下载,可据此梳理主备切换与复制参数配置思路。
1. 主库宕机业务跟着停摆:这套 PostgreSQL 主从流复制加 pgpool 方案解决的是什么
一台数据库服务器在凌晨两点电源故障,应用连接串里写死的那一个 IP 再也连不上,值班的人只能翻文档、改配置、重启服务,业务停摆四十多分钟。这类事故的根子不在数据库本身,而在于单点。这份《Postgres主从流复制+pgpool高可用方案》给的就是从单点到主备的一整套落地路径:用 PostgreSQL 自带的流复制把 WAL 日志实时送到备库,用 pgpool-II 做连接池、读写分离和故障切换,再叠一层 pg_rman 的离线全量与归档备份兜住误删误改。它面向的是 RHEL / CentOS 系发行版上跑中小业务、运维人手只有一两个人的场景——想用开源组件把数据库可用性抬上去,又不想引入商业套件。写入量极大、要求跨机房强一致的场景,这套东西撑不住,得另想办法。
2. 装库之前先把系统层铺平:用户、目录、内核参数与免密登录
很多人拿到方案直接跳到第五章装 PostgreSQL,结果在初始化实例时被共享内存报错、被权限问题卡住、被防火墙拦在第一步。系统层这层土没夯实,后面每一步都会返工。我把这章放在最前面,是因为实际实施里它占的时间比装数据库还长。
2.1 版本怎么搭:三个组件的对应关系
版本搭配是这套方案里最容易埋雷的地方。这份资料里给了两组环境:PostgreSQL 10.3 配 pgpool-II 3.6.14,PostgreSQL 12.2 配 pgpool-II 4.1.1。两组不是随便配的,pgpool-II 每个大版本对 PostgreSQL 的支持范围是有限的,3.6 系列吃不下 PG 12 的部分新特性,4.1 系列才是 PG 12 的对口选择。主备两台机器的 PostgreSQL 版本必须完全一致,小版本也建议一致,否则 WAL 格式、系统表结构出现差异时,备库起来会直接报错。
| 组件 | 旧环境版本 | 新环境版本 | 部署位置 | 作用 |
|---|---|---|---|---|
| PostgreSQL | 10.3 | 12.2 | 主库、备库 | 数据存储与流复制 |
| pgpool-II | 3.6.14 | 4.1.1 | 主库、备库各一套 | 连接池、读写分离、故障切换 |
| pg_rman | 与 PG 大版本对应 | 与 PG 大版本对应 | 主库 | 全量/增量备份与恢复 |
pgpool-II 装在主备两台机器上而不是单独一台,是因为要用 watchdog 做双机互检,虚拟 IP 在两者之间漂移。这一点和"中间件单独一台机器"的常见架构不一样,单机部署 pgpool 反而会引入新的单点。
我一般会把安装目录统一成/opt:PostgreSQL 装/opt/pgsql,pgpool 装/opt/pgpool,备份放/opt/pg_backup。统一路径的好处是后面写脚本时不用为每台机器改路径,pg_rman 的归档目录、备份目录也能直接写死。
2.2 建 postgres 用户、划目录、给权限
所有节点都先做这一步。数据库进程不允许用 root 跑,这是硬约束,不是建议。
# 三个节点统一执行:建用户、建目录、给权限 groupadd -g 1100 postgres useradd -g postgres -u 1100 -m -d /home/postgres -s /bin/bash postgres echo 'Pg@2024#01' | passwd --stdin postgres # 数据库软件目录、数据目录、归档目录、备份目录 mkdir -p /opt/pgsql/{app,data,archive,backup} mkdir -p /opt/pgpool/{app,etc,log} chown -R postgres:postgres /opt/pgsql /opt/pgpool chmod 700 /opt/pgsql/data # 数据目录只允许属主进入 chmod 750 /opt/pgsql/archive # 归档目录允许同组读/opt/pgsql/data必须是 700,PostgreSQL 启动时会检查这个权限,权限过宽会直接拒绝启动,这是initdb的安全机制。归档目录给 750 是因为 pg_rman 要做校验,有时候还需要运维同组账号进去看文件。密码这一行在离线环境里可以直接用交互式passwd,脚本化时才用--stdin。
这里有个容易被忽略的细节:如果主备两台机器的 postgres 用户 UID 不一致,后面用scp或rsync搬文件时会出属主错乱。脚本里写死-u 1100就是为了避免这个问题,两边 UID 一致,文件传过去不用再 chown。
2.3 防火墙、selinux、hosts 与时钟
这四项属于"不配不一定出事,出事一定查半天"的类型。pgpool 的 watchdog 靠主机名互相通信,hosts 里名字解析不了,watchdog 就起不来,而报错信息往往只告诉你心跳超时。
# 关闭防火墙与 selinux(内网受控环境下的常见做法) systemctl stop firewalld && systemctl disable firewalld setenforce 0 sed -i 's/^SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/config # 主机名与 hosts,两台机器都要有一模一样的条目 hostnamectl set-hostname pg-master cat >> /etc/hosts <<'EOF' 10.0.0.11 pg-master 10.0.0.12 pg-standby 10.0.0.13 pg-vip EOF # 时钟同步,两台机器必须一致 yum install -y chrony systemctl enable --now chronyd chronyc sources -v时钟同步单独拿出来说:流复制本身对时钟不敏感,但 pgpool 的健康检查和故障判定依赖时间戳,两台机器时间差超过几秒时,watchdog 会出现双方都认为对方死掉的双主风险。chrony 装完务必用chronyc sources确认源是^*状态。
防火墙如果必须开,那至少放行三个端口:PostgreSQL 的 5432(主备复制和 pgpool 后端连接)、pgpool 的 9999(客户端接入)、pgpool 的 9898(watchdog 心跳,配置里可改)。
2.4 sysctl 与 limits:共享内存和文件句柄
PostgreSQL 启动时会把shared_buffers申请成一块共享内存,kernel.shmmax小于这个值时,pg_ctl start会失败并提示 could not create shared memory segment。这个坑在新机器上几乎必踩。
cat >> /etc/sysctl.conf <<'EOF' kernel.shmmax = 68719476736 kernel.shmall = 4294967296 kernel.sem = 50100 64128000 50100 1280 vm.overcommit_memory = 2 vm.swappiness = 10 EOF sysctl -p cat >> /etc/security/limits.conf <<'EOF' postgres soft nofile 65536 postgres hard nofile 65536 postgres soft nproc 65536 postgres hard nproc 65536 EOFshmmax设成 64GB,够覆盖绝大多数中小业务的shared_buffers配置(一般设物理内存的 25%)。shmall是共享内存总页数,要和 shmmax 匹配,设小了仍然会启动失败。vm.overcommit_memory=2是严格模式,能防止内存超卖导致 OOM killer 杀掉数据库进程,但要求你对机器上跑的东西心里有数;拿不准就用默认的 0。nofile给到 65536,是因为 pgpool 的连接池在高峰期会占大量句柄,默认的 1024 根本不够。
3. 流复制落地:从 postgresql.conf 到 pg_basebackup 的完整链路
流复制的本质是主库的 walsender 进程把 WAL 记录通过 TCP 推给备库的 walreceiver,备库的 startup 进程再把收到的 WAL 重放,本地数据就跟主库对上了。理解了这三个进程的分工,参数怎么调心里就有数:walsender 受max_wal_senders限制,walreceiver 的连接信息写在primary_conninfo里,重放速度决定了备库延迟。
3.1 主库必改的参数与 pg_hba 的一条放行规则
主库的postgresql.conf改动集中在复制、归档、连接三块。我用 12 版本的写法,10 版本把wal_keep_size换回wal_keep_segments即可。
# /opt/pgsql/data/postgresql.conf —— 主库 listen_addresses = '*' # 至少包含主备内网 IP wal_level = replica # 必须有 replica 及以上,否则不开 walsender max_wal_senders = 10 # 并发 walsender 数,主备+pg_basebackup 共用 wal_keep_size = 1GB # 12 起用这个,保留 1GB WAL 给备库追 archive_mode = on archive_command = 'test ! -f /opt/pgsql/archive/%f && cp %p /opt/pgsql/archive/%f' hot_standby = on # 备库可读,读写分离的前提 synchronous_commit = on synchronous_standby_names = 'ANY 1(pg-standby)'synchronous_standby_names这一行决定了同步还是异步。留空就是异步,主库提交不等备库,延迟低但主库崩溃时可能丢掉最后几百毫秒的事务。写成ANY 1(pg-standby)是同步,主库要等至少一个备库确认收到 WAL 才返回提交成功,牺牲一点写延迟换数据不丢。这里用ANY而不是直接写名字,是为了将来加第二个备库时不用改配置——任意一个备库确认就算数。application_name要和备库的primary_conninfo里写的一致,否则主库认不出是谁,同步复制会一直挂着等。
archive_command里那个test ! -f是防止重复归档同一个文件时报错导致归档卡住,这是很常见的一个小加固。
pg_hba.conf只需要加两条,一条给复制连接,一条给业务连接:
# /opt/pgsql/data/pg_hba.conf —— 追加在文件末尾 host replication repl 10.0.0.0/24 md5 host all all 10.0.0.0/24 md5第一行的replication是伪数据库名,专门用于流复制连接,不能写成all。第二行放行整个内网段,是因为 pgpool 所在的两台机器都要连过来。如果 pgpool 和后端数据库不在同一网段,这里要单独加 pgpool 的 IP。
3.2 建复制账号与 pg_basebackup 初始化备库
主库启动之后第一件事是建复制账号。这个账号只需要REPLICATION权限,不要给超级用户,减少被滥用的面。
-- 主库执行 CREATE ROLE repl WITH REPLICATION LOGIN PASSWORD 'Repl@2024'; SELECT rolname, rolreplication FROM pg_roles WHERE rolname = 'repl';然后在备库上执行基础备份,把主库的数据目录整份拉过来。12 版本用-R参数会自动写好 standby 标志和连接信息,比手工写recovery.conf省事得多。
# 备库执行,先把数据目录清空 su - postgres rm -rf /opt/pgsql/data/* pg_basebackup -h 10.0.0.11 -p 5432 -U repl -D /opt/pgsql/data \ -Fp -Xs -P -R -v-Fp是普通文件格式,-Xs表示流式传输 WAL(避免备份过程中主库 WAL 被清理导致备份不完整),-P显示进度,-R是关键:它在数据目录里生成standby.signal空文件,并把primary_conninfo写进postgresql.auto.conf。12 以前的版本没有-R的完整效果,需要手工创建recovery.conf,内容形如standby_mode = 'on'、primary_conninfo = 'host=10.0.0.11 port=5432 user=repl password=...',主库提升时这个文件会被自动改名为recovery.done——这也是为什么老版本升级到 12 时经常找不到recovery.conf。
备库的postgresql.conf里还要补两行:hot_standby = on(允许只读查询)和primary_conninfo里带上application_name=pg-standby,名字与主库的synchronous_standby_names对齐。
3.3 复制建好之后怎么验:三个视图和一条延迟算法
服务起来不等于复制正常。我见过备库进程活着但一直没收到 WAL 的情况,最后发现是pg_hba少了一行。验收要看三处。
-- 主库:看 walsender 和同步状态 SELECT pid, usename, application_name, client_addr, state, sync_state, sent_lsn, write_lsn, flush_lsn, replay_lsn FROM pg_stat_replication;state应该是streaming,sync_state在同步复制下是sync,异步下是async。如果这张表是空的,说明备库根本没连上来,去备库日志里找连接失败原因,八成是pg_hba.conf或密码。
-- 主库看当前 LSN,备库看已重放 LSN,两者差值就是延迟 -- 主库 SELECT pg_current_wal_lsn(); -- 备库 SELECT pg_last_wal_replay_lsn(), pg_last_xact_replay_timestamp();把两个 LSN 用pg_wal_lsn_diff()一减就得到字节延迟。pg_last_xact_replay_timestamp()更直观,它表示备库最后一次应用事务的时间,和当前时间相减就是"备库落后多少秒"。这个值在空闲库上会很大(因为没有事务可重放),别误判成故障,压一点测试流量再看。
3.4 主备切换:promote 与 pg_rewind
计划内切换和故障切换的操作路径不一样。计划内切换可以先确认备库追平,再提升;故障切换只能直接提。
# 备库提升为主库 su - postgres /opt/pgsql/app/bin/pg_ctl promote -D /opt/pgsql/data # 日志中出现 "database system is ready to accept connections" 即完成提升之后,原来的主库如果想重新加入集群,两条路:一是用pg_basebackup全量重做,简单但耗时;二是用pg_rewind增量回滚,快但要求主库开启过wal_log_hints = on或者建库时用了--data-checksums。没有这两个前提,pg_rewind会直接拒绝执行,这个限制在故障当天才发现就晚了,建议建库时就把wal_log_hints打开。
4. pgpool-II 接上去:连接池、读写分离与 pcp 管理口的配置取舍
流复制解决了数据同步,但客户端还是得知道连哪台。pgpool-II 的价值在于把两台库藏在 9999 端口后面:应用只认一个地址,pgpool 负责把写发到主库、把读分散到备库、主库挂了自动摘掉。它是中间件不是数据库,配置错了不会报语法错,只会表现为"连接莫名变慢"或"读到了旧数据",所以参数必须一个个对着业务量调。
4.1 pgpool.conf 里真正影响行为的参数
配置文件很长,几百行,但真正决定行为的就那么十几个。下面这段是双节点流复制模式的骨架。
# /opt/pgpool/etc/pgpool.conf listen_addresses = '*' port = 9999 pcp_port = 9898 backend_hostname0 = '10.0.0.11' backend_port0 = 5432 backend_weight0 = 1 backend_flag0 = 'ALWAYS_PRIMARY|DISALLOW_TO_FAILOVER' backend_application_name0 = 'pg-master' backend_hostname1 = '10.0.0.12' backend_port1 = 5432 backend_weight1 = 1 backend_flag1 = 'DISALLOW_TO_FAILOVER' backend_application_name1 = 'pg-standby' enable_pool_hba = on pool_passwd = 'pool_passwd' connection_cache = on max_pool = 4 num_init_children = 100 load_balance_mode = on master_slave_mode = on master_slave_sub_mode = 'stream' health_check_period = 10 health_check_timeout = 20 health_check_user = 'health_check' health_check_password = 'health@2024' health_check_database = 'postgres' sr_check_period = 10 sr_check_user = 'sr_check' sr_check_password = 'sr@2024' sr_check_database = 'postgres'backend_flag0里的ALWAYS_PRIMARY是 pgpool 4.x 的写法,告诉它 0 号节点永远是首选主库,DISALLOW_TO_FAILOVER表示不把这个节点当作可切换目标(它自己就是主)。backend_application_name要和主库postgresql.conf里的名字对上,pgpool 靠这个在流复制拓扑里识别角色。
max_pool和num_init_children相乘决定了对外能承载的最大连接数:100 个子进程 × 每个 4 条池化连接 = 400。这两个数要和后端 PostgreSQL 的max_connections匹配,配大了会把后端打爆,配小了客户端排队。我一般按后端max_connections的 60% 反推,留出余量给主备复制和人工维护。
health_check_user和sr_check_user必须是后端真实存在的账号,且要能在pg_hba.conf允许的网段内登录。这两个账号只用来探活,给最普通的登录权限就够,别图省事用 postgres 超级用户。
4.2 认证三件套:pool_hba、pool_passwd、pcp.conf
pgpool 自己有一套认证,和后端 PostgreSQL 的认证是两层。客户端连 9999 时先过 pool_hba,再过 pool_passwd,最后才是后端库的 pg_hba。三层有一层没配通,表现都是"密码错误"或"连接被拒"。
# 1. pool_hba.conf:放行客户端网段 cat >> /opt/pgpool/etc/pool_hba.conf <<'EOF' host all all 10.0.0.0/24 md5 EOF # 2. pool_passwd:写入数据库用户的 md5 密码 cd /opt/pgpool/etc /opt/pgpool/app/bin/pg_md5 -p -m -f pool_passwd postgres # 3. pcp.conf:pcp 管理命令的口令 /opt/pgpool/app/bin/pg_md5 -p -m -f pcp.conf pgpool_adminpg_md5 -m表示用 md5 方式,-p会交互式提示输入密码。写进pool_passwd的账号必须和客户端实际使用的数据库账号一致,postgres只是一个例子。pcp.conf里的账号是给pcp_node_info、pcp_attach_node、pcp_recovery_node这些管理命令用的,和数据库账号不是一回事,别混着配。
pcp 命令是 pgpool 运维的主要入口,几条常用的记牢:
# 查看后端节点状态,0 是主,1 是备,status 为 up/down /opt/pgpool/app/bin/pcp_node_info -h 10.0.0.11 -p 9898 -U pgpool_admin # 手动把掉线的备库挂回去 /opt/pgpool/app/bin/pcp_attach_node -h 10.0.0.11 -p 9898 -U pgpool_admin -n 1 # 在线恢复备库(配合 recovery_1st_stage_command 使用) /opt/pgpool/app/bin/pcp_recovery_node -h 10.0.0.11 -p 9898 -U pgpool_admin -n 1pcp_recovery_node是这套方案里最值钱的一条命令,它能让备库在故障恢复后自动重建、自动追平、自动挂回集群,不需要人工跑一遍 basebackup。前提是 pgpool.conf 里配好了recovery_1st_stage_command,指向一个在备库上执行恢复的脚本。
4.3 failover 脚本、watchdog 与虚拟 IP
主库挂掉之后,pgpool 要做两件事:把备库提升为主,把虚拟 IP 从故障节点挪走。这两件事靠 failover_command 和 watchdog 配合完成。
# 故障时执行的脚本,%m 旧主节点 ID,%H 新主节点主机名 failover_command = '/opt/pgpool/etc/failover.sh %m %H %P %d' follow_master_command = '/opt/pgpool/etc/follow_master.sh %m %H' # watchdog:双机互检 + 虚拟 IP 漂移 use_watchdog = on hostname0 = 'pg-master' wd_port0 = 9000 pgpool_port0 = 9999 hostname1 = 'pg-standby' wd_port1 = 9000 pgpool_port1 = 9999 delegate_IP = '10.0.0.13' ifconfig_path = '/sbin' if_up_cmd = 'ifconfig eth0:0 inet $_IP_$ netmask 255.255.255.0' if_down_cmd = 'ifconfig eth0:0 down' arping_path = '/usr/sbin' arping_cmd = 'arping -U $_IP_$ -w 1 -I eth0'if_up_cmd和arping_cmd里这两个$_IP_$是 pgpool 的占位符,运行时会被替换成delegate_IP,别手抖改了。arping那一步是给交换机刷新 ARP 表用的,少了它,虚拟 IP 漂移过去了但客户端还在往旧机器发包,现象是"切换成功但业务连不上",非常难查。
执行这些命令需要 root 权限,而 pgpool 一般以普通用户运行,常见做法是把这三条命令写进/etc/sudoers的 NOPASSWD 白名单,并在 pgpool.conf 的if_up_cmd前面加sudo。sudoers 里必须精确到命令行,不要图省事给 ALL。
4.4 验证读写分离是不是真的生效了
配完不验证等于没配。最直接的方式是通过 9999 端口连进去,看查询落在哪台机器上。
-- 通过 pgpool 的 9999 端口连接 SELECT inet_server_addr(), inet_server_port(); -- 这条不会走 pgpool 的负载均衡,因为 inet_server_addr 不是普通查询 SHOW pool_nodes; -- 节点状态、角色、复制延迟 SHOW pool_status; -- 全部运行参数,用来确认配置生效 SHOW pool_pools; -- 连接池使用情况SHOW pool_nodes输出里每行有status、role、replication_delay三列。role显示 primary / standby,replication_delay是 pgpool 自己算出来的延迟。如果status一直是down而库里明明活着,八成是health_check_user登不进去,去 pgpool 日志里搜health check failed能看到具体原因。
读写分离的验证要靠执行计划和日志。开log_statement = 'all'太吵,我一般开log_min_duration_statement = 200,然后把log_perror = on打开,在 pgpool 日志里看DB node id: 1这类记录,确认 select 确实被发到了 1 号节点。
5. 避坑与排查:failover 演练里最常见的五个翻车现场
配置写完只是开始,真正的成本在演练和排错。下面这五条都是我实际踩过或者帮人收拾过的,按出现频率排。每条的现象都很有迷惑性,光看报错很容易走偏。
5.1 备库起不来,日志只说找不到 recovery.conf
现象:备库执行pg_ctl start,日志里一行FATAL: could not open file "recovery.conf"或者干脆静默退出,进程列表里连 startup 进程都没有。
原因是版本差异。PostgreSQL 12 开始彻底移除了recovery.conf,改用数据目录下的standby.signal空文件加postgresql.conf(或postgresql.auto.conf)里的primary_conninfo。方案文档里同时给了"12.X 以前版本"和"12.X 以后版本"两套写法,照抄错版本就会这样。
解决:确认PG_VERSION文件里的大版本号,12 及以上只建standby.signal,把连接串写进postgresql.auto.conf。用pg_basebackup -R的话这一步是自动完成的,所以能用-R就别手工写。
5.2 连接报 no pg_hba.conf entry for host
现象:客户端连 9999 端口,或者 pgpool 连后端 5432,报no pg_hba.conf entry for host "10.0.0.11", user "repl", database "replication"。
原因有三层,要按层排查。第一层是后端的pg_hba.conf没放行 pgpool 所在机器的 IP;第二层是放了行但数据库名写成了all而不是replication,流复制连接匹配不上;第三层是 pgpool 自己的pool_hba.conf没放行客户端。三层都长得一样,只能一层层试。
解决:先确认报错发生在哪一跳——报错信息里的 host 如果是客户端 IP,说明卡在 pool_hba;如果是 pgpool 的 IP,说明卡在后端 pg_hba。改完pg_hba.conf一定要pg_ctl reload,这个文件支持 reload 不需要重启。
5.3 pool_nodes 里节点全 down,但数据库明明活着
现象:SHOW pool_nodes两行都是 down,客户端连不上,可手动psql -h 10.0.0.11 -U postgres完全正常。
原因是health_check_user这个账号不存在,或者密码和health_check_password对不上。pgpool 探活失败就判定节点死掉,直接摘掉,哪怕后端服务好好的。另一个常见原因是探活账号在pg_hba.conf里没被放行,密码再对也没用。
解决:手工用health_check_user登录一次后端库,能进说明账号没问题;进不去就看pg_hba。确认后从 pgpool 日志里搜health check failed,它会明确告诉你失败在哪一步。改完配置要重启 pgpool,健康检查参数不支持 reload。
5.4 虚拟 IP 漂了,业务还是连不上
现象:主库宕机后,ip addr在新节点上能看到 VIP,但客户端连接依然超时或者连到旧机器。
原因通常是三个。一是arping_cmd没执行成功,交换机 ARP 表还是旧 MAC,包被送到已经下线的机器;二是if_up_cmd里的网卡名和实际不符,比如配的是eth0而机器上是ens33;三是 sudo 权限没给对,pgpool 日志里能看到Permission denied但被淹没在大量心跳日志里。
解决:先在新节点上手工执行一遍if_up_cmd和arping_cmd的命令体,确认能配上地址;再检查 sudoers;最后用一个不相关的客户端持续 ping VIP,切换时观察丢包多久恢复。这一步必须在演练环境做,生产上第一次发现就晚了。
5.5 同步复制下备库一停,主库写入直接卡死
现象:备库因为维护停机,主库的 insert 全部挂住,应用超时,主库 CPU 不高但连接堆积。
原因是synchronous_commit = on加synchronous_standby_names配了固定名字,主库会一直等那个备库确认。备库不在线,事务就永远提交不了。这在异步复制下不会发生,所以很多人第一次遇到会怀疑是锁或者磁盘问题。
解决:短期把synchronous_standby_names改成ANY 1(pg-standby, pg-standby2),任意一个在线即可;长期要么准备第二个备库,要么评估能不能接受异步复制的少量数据丢失风险。还有一个办法是把synchronous_commit降成local,但这就等于放弃了同步语义,得和业务方确认。
6. pg_rman 补上后悔药:归档、全量与恢复演练
流复制扛的是机器故障,扛不住DELETE忘写WHERE。方案里第十一章的 pg_rman 就是为这个准备的——它把全量备份、增量备份和 WAL 归档串起来,能恢复到任意时间点。配置的前提是主库已经开了archive_mode = on并且archive_command真的在工作,这一点可以用下面的语句验证,返回值必须是on且归档目录里有文件持续产生。
SELECT name, setting FROM pg_settings WHERE name IN ('archive_mode','archive_command'); SELECT last_archived_wal, last_archived_time FROM pg_stat_archiver;pg_rman 的日常使用就四条命令:
# 初始化备份目录(只需一次) pg_rman init -B /opt/pgsql/backup --with-serverlog # 全量备份 + 校验 pg_rman backup -B /opt/pgsql/backup -D /opt/pgsql/data --backup-mode=full -C pg_rman validate -B /opt/pgsql/backup # 恢复到指定时间点(需先停库) pg_rman restore -B /opt/pgsql/backup -D /opt/pgsql/data \ --recovery-target-time='2024-05-20 03:00:00'--with-serverlog把数据库日志一起备进去,排查恢复问题时很有用。-C是备份后立即校验,不校验的备份等于没有备份。恢复时--recovery-target-time精确到秒,恢复到误操作之前的那一刻,然后把数据导出来再导回生产库——不要直接拿恢复库顶上生产,中间还差一次数据核对。
备份策略我用的是每周日全量、周一到周六增量、归档持续保留两周。脚本层面就是两个 shell,一个全量一个增量,挂 cron 跑,跑完把pg_rman show的输出重定向到日志文件,第二天早上扫一眼。表结构上值得记一下 pg_rman 的三个目录:backup/存备份集、arclog/存归档、srvlog/存服务器日志,恢复时三个目录缺一不可。
真正让我养成习惯的是一次演练:备份脚本天天跑成功,但从没做过恢复验证,真要用的时候才发现归档目录权限不对,恢复卡在第二个 WAL 文件上。从那以后我每次改完备份配置,都强制走一遍"备份 → validate → 恢复到临时目录 → 起库 → 比对行数"的完整链路,不管多麻烦。这套方案的脚本和配置在资源包里都有现成的,按上面的顺序走一遍,比自己从零拼要快得多。希望帮到你。
本文还有配套的精品资源,点击获取