用pg_rman备份集快速搭建PostgreSQL流复制备库实战 📅 发布时间:2026/9/18 3:15:50 👁 浏览次数: 1. 这套方案的定位与适用场景先把我为什么推荐这个组合讲清楚。PostgreSQL的高可用方案里流复制streaming replication是绝对的主流也是绝大多数生产环境的选择。它不是最复杂的方案但却是最可靠、最成熟、最容易被团队接手的方案。而搭建备库的方式有好几种有从零pg_basebackup拉全量的有直接拷贝数据目录的也有像标题里这样用pg_rman物理备份来恢复成备库的。那为什么有人放着更简单的pg_basebackup不用偏要绕一圈用pg_rman我在实际运维中遇到的情况是这样的很多团队的备份体系已经跑了好几年pg_rman的备份集是现成的每天全备、每小时增备归档日志也都齐。这时候如果新加一台备库你没必要再跑一次全量pg_basebackup去占用主库的IO和带宽直接拿昨晚的备份集恢复到新机器上再把归档和流复制通道一接一个备库就起来了。这个思路本质上是在“复用现有备份资产”。这套方案适合谁适合那些已经有pg_rman备份体系的团队或者正在规划备份体系、希望一套备份同时兼顾“灾难恢复”和“快速搭建备库”两种诉求的团队。我自己在管理十几套PostgreSQL实例时一直沿用这个套路——备份集不只躺在那里等灾难发生它更是一个随时可用的“备库种子”。顺带说一句很多人纠结“到底用pg_basebackup还是用备份集恢复”我的建议很简单如果主库压力允许pg_basebackup确实更省事但如果你的备份链路本身已经很完善那从备份集恢复备库完全是顺手的事而且不打扰主库。两种方案我都跑过没有绝对优劣看你的基础设施走到了哪一步。2. 流复制备库的底层逻辑物理备份为什么能直接变成备库2.1 备库的本质一份能追日志的全量数据副本很多人对备库的理解停留在“复制一份数据过去”但真正动手时往往会卡住为什么我直接把数据目录拷贝过去备库起不来为什么日志对不上这些问题的根源在于没有理解备库的本质。PostgreSQL的流复制备库说白了就是一份与主库完全一致的物理数据文件加上一个不停接收并应用WAL日志的进程。主库上任何一次数据变更都会先写WAL日志然后通过流复制协议发送给备库备库拿到日志后重放到自己的数据文件里。所以备库要成立必须满足两个条件第一它的基础数据文件和主库在某个时间点是完全一致的第二它从那个时间点开始能拿到完整的WAL日志序列。这就是为什么“直接把数据目录复制过去”经常失败——你拷贝的瞬间主库还在不停写数据文件处于一个不一致的状态日志也没有对应的起点。pg_rman物理备份解决了第一个问题。它通过“全量备份归档日志”的组合保证恢复出来的数据目录是一个内部一致的、有明确日志起点的状态。解决了第二个问题就是接下来要讲的流复制配置。2.2 pg_rman备份与普通物理拷贝的核心差异用过pg_rman的人都知道它有一个很重要的概念叫“备份时间线”backup timeline。备份时它不只是把数据文件拷走还会记录这个备份对应的WAL位置通过pg_start_backup和pg_stop_backup或者新版本里的pg_backup_start/pg_backup_stop。这意味着恢复时PostgreSQL能明确知道从哪个日志位点开始继续追。这一点非常关键。普通拷贝没有这个位点概念就算你rsync完数据文件备库也不知道该从哪条日志开始。而pg_rman的备份集里backup.ini文件明确记录了START_LSN、STOP_LSN等信息恢复完成后PostgreSQL就可以从recovery.signalPG 12或recovery.confPG 12之前里配置的restore_command和primary_conninfo继续接日志。所以你可以这样理解pg_rman的备份集是一份已经打好了“日志续接锚点”的完整数据副本。而流复制备库恰恰需要这个锚点。我用一个生活化的类比来解释主库是一本一直在写的账本WAL日志就是每天新增的流水。pg_rman做的事情是找一个时间点把账本复印一份同时在复印件上标注“这是截至某天的账目”。备库就是拿到这份复印件然后每天继续接收新的流水誊写到自己的复印件上。没有那个“截至某天”的标注新流水根本不知道该从哪一页开始。2.3 为什么备库恢复时还要走一遍“恢复流程”很多人第一次用pg_rman恢复备库时会觉得奇怪我明明已经用pg_rman restore把数据文件放回去了为什么还要配置recovery.signal才能启动这不是多此一举吗其实不是。pg_rman restore做的事情是把备份集里的物理文件放回数据目录但PostgreSQL并不知道这份数据是“要用来当备库的”。通过创建recovery.signal文件并且配置primary_conninfo和restore_command你是在告诉PostgreSQL启动后不要直接对外提供服务而是先进入恢复模式回到备份位点然后连接主库继续接收WAL。这就是流复制备库的启动流程。在PG 12以前这个配置写在recovery.conf里PG 12以后拆成了recovery.signal或standby.signal加postgresql.conf里的参数。别小看这个变化很多从老版本升级上来的同学会习惯性地去找recovery.conf结果发现目录里根本没有这个文件备库一直起不来。后面我会专门写一节排查这类问题。3. 实操前置从归档配置到完整备份链路3.1 主库的WAL归档为什么是必要前提要利用pg_rman备份搭建备库主库必须先开启WAL归档。这是整个方案的基石。很多人只开了流复制没开归档然后想着“我直接用pg_rman全量备份恢复就行了”结果恢复完后备库追日志追到一半就断了——因为主库的WAL已经被循环覆盖了备库没来得及接收的那些日志已经不存在了。PostgreSQL默认的WAL段大小是16MBwal_keep_size老版本是wal_keep_segments控制着主库额外保留多少WAL给备库。如果备库落后太多主库上保留的WAL不够备库就彻底追不上了只能重新搭。为了避免这种情况归档是必须的——备库追不上时可以从归档目录里补日志。所以我的建议是主库的postgresql.conf里archive_mode on必须打开archive_command要配置好。用pg_rman的话常见做法是配合pg_rman自己的归档管理或者用传统的cp或rsync命令把归档日志传到独立目录或存储。操作系统层面不用担心不兼容archive_command本质上就是一条shell命令能跑通就行。pg_rman甚至有一个直接搭档的功能pg_rman archive它可以把WAL归档文件纳入自己的备份管理体系中删除备份时自动判断哪些归档还能删、哪些必须保留。这一点后面细说。3.2 权限与账号主库需要什么角色给备库用流复制备库要连接主库拉取WAL需要一个专门的复制账号。PostgreSQL里这个账号必须有REPLICATION属性。我见过不少新手直接用超级用户去连也能通但不推荐——权限最小化是运维的基本素养。创建复制账号的SQL很简单CREATE ROLE replicator WITH REPLICATION LOGIN PASSWORD your_strong_password;注意LOGIN属性是必须的否则无法远程连接。REPLICATION属性赋予了这个账号执行流复制协议的权限。有些团队还会加上CONNECTION LIMIT来限制并发连接数比如CONNECTION LIMIT 5防止账号被滥用。另外主库的pg_hba.conf里要放行备库IP的复制连接。一般这样写host replication replicator 192.168.1.0/24 md5这里replication是数据库名位置的特殊关键字表示这条规则只针对流复制连接。千万注意不要写成了host all replicator ...否则备库连接时会报权限错误因为流复制连接走的是复制协议不走普通数据库连接。改完pg_hba.conf后要执行SELECT pg_reload_conf();让配置生效不需要重启数据库。3.3 一次完整的pg_rman全量备份在搭建备库之前我们先确保主库上有一套完整的pg_rman备份。这里假设你已经安装好了pg_rman并且初始化好了备份目录。如果没有先做一步初始化pg_rman init -B /backup/pg_rman-B指定备份目录这个目录会存放全量备份、增量备份和WAL归档。建议跟数据目录分开放在独立的磁盘或存储上否则磁盘满了会连累数据库本身。接着执行全量备份。pinpoint一个关键点pg_rman的全量备份默认就是在线备份不需要停库。命令如下pg_rman backup -B /backup/pg_rman -b full -Z -C参数拆解一下-b full全量备份。-Z启用压缩节省磁盘。除非你的磁盘非常充裕否则我建议开着。-C备份完成后进行校验检查备份文件是否完整。这一步在搭建备库前特别重要如果备份本身不完整后面恢复时会浪费很多排查时间。备份完成后用pg_rman show确认备份集状态pg_rman show -B /backup/pg_rman输出里会有一个STATUS列OK表示备份有效DONE表示备份已完成。如果状态是RUNNING或者ERROR说明备份没成功不要继续往下走。备份产生的backup.ini文件里会记录关键位点信息恢复时要靠它。文件内容大致长这样BACKUP_MODEFULL START_LSN0/2C000028 STOP_LSN0/2C000100 START_TIME2024-11-20 02:00:00 STOP_TIME2024-11-20 02:15:00这些信息就是前面提到的“日志续接锚点”。在流复制场景下备库从STOP_LSN之后开始接主库的WAL。4. 从备份恢复到备库启动完整实操过程4.1 环境准备新机器上的PostgreSQL安装与目录规划备库所在的新机器首先要安装与主库大版本一致的PostgreSQL。什么叫大版本一致PG 16的备份不能恢复到PG 15的实例上小版本可以不一致比如主库PG 16.4备库PG 16.1可以但建议尽量保持一致减少意外。另外备库的目录规划尽量与主库一致尤其是数据目录和表空间路径。如果路径不一致恢复时还需要调整表空间映射多一事不如少一事。我在生产环境里都是直接沿用同一套目录规范包括/data/pgdata、/backup/pg_rman这些路径两台机器完全对齐。这样连配置文件的对比都不用动脑子。安装完成后不要急着初始化数据库。我们不需要initdb生成的数据目录因为恢复出来的数据会直接覆盖它。如果你已经初始化了也没关系恢复时会把数据目录清空重写但注意pg_rman restore默认只恢复数据文件不会动配置文件所以配置文件要自己准备好。4.2 用pg_rman restore把数据放回备库把主库备份集同步到备库机器上或者直接把备库机器的/backup/pg_rman挂载到主库备份存储上NFS等方式然后执行恢复。恢复之前先停掉备库的PostgreSQL服务如果已经在运行的话确保数据目录没有被占用。然后执行pg_rman restore -B /backup/pg_rman -D /data/pgdata-D指定备库的数据目录。这条命令会把备份集里的所有数据文件解压并放回/data/pgdata。如果你用了表空间pg_rman会尝试恢复表空间路径但前提是路径存在于备库机器上。所以表空间路径也需要提前建好或者用-T选项做映射。恢复完成后强烈建议做两件事用pg_rman validate校验一下备份集的完整性确认恢复没问题。权限检查确保/data/pgdata目录的属主是数据库运行用户通常是postgres否则启动时会报权限错误。chown -R postgres:postgres /data/pgdata这一步被很多人忽略尤其在用root操作时恢复出来的文件属主全是root数据库一启动就报could not open file之类的错误。4.3 备库配置primary_conninfo、恢复信号与归档补日志数据文件就位后接下来是关键配置。这里的配置决定备库能否连接主库、能否补上备份点到当前时刻的日志。以PG 16为例在/data/pgdata/postgresql.conf里配置primary_conninfo host主库IP port5432 userreplicator password你的密码 application_namestandby01 restore_command cp /backup/archive/%f %pprimary_conninfo是备库连接主库的通道application_name建议填写备库的唯一标识后面做主库监控时通过pg_stat_replication可以清楚看到是哪台备库。restore_command这里用最简单的cp命令示意实际生产环境可能用rsync或者从对象存储拉取只要能根据%f日志文件名找到对应的归档文件并复制到%p目标路径即可。然后创建恢复信号文件。PG 12的机制是如果数据目录里存在standby.signal实例启动后会以备库模式运行。命令很简单touch /data/pgdata/standby.signal注意standby.signal文件必须属于数据目录属主一般也是postgres用户。这个文件里不需要写内容它的存在本身就是一种配置。另外PG 12以前的版本是编辑recovery.conf里面写standby_mode on现在的版本不需要了。还有一个细节hot_standby参数。这个参数控制备库是否允许只读查询。生产环境建议开启否则备库只能接收日志没法查数据高可用和读写分离都无从谈起。PG 12默认就是on但我见过一些从老版本迁移过来的配置文件里显式设置了off这点要留意。4.4 启动备库并观察流复制状态配置完成后正常启动备库systemctl start postgresql或者用pg_ctl start -D /data/pgdata取决于你的启动方式。启动后不要急着认为就成功了一定要观察日志。日志文件一般在/data/pgdata/log下或者journalctl -u postgresql。正常启动的备库日志里会有这样的关键信息LOG: starting point-in-time recovery LOG: database system was interrupted; last known up at 2024-11-20 02:15:00 CST LOG: started streaming wAL to primary at 0/30000000 on timeline 1 LOG: redo starts at 0/2C000028started streaming WAL to primary这行字出现基本就成功了。备库已经开始向主库请求WAL日志。然后登录备库查看流复制状态SELECT * FROM pg_stat_wal_receiver;或者去主库上看SELECT * FROM pg_stat_replication;pg_stat_replication在主库上能看到所有连接的备库关键字段包括application_name备库的名字对应配置里的application_name。statestreaming表示正在流复制。sent_lsn、write_lsn、flush_lsn、replay_lsn分别表示主库发送、备库写入、备库落盘、备库重放的日志位点。replay_lag备库落后主库的时间差。如果state是catchup说明备库正在补日志还没追平变成streaming并且replay_lag接近0就说明备库已经追平了。4.5 验证数据一致性不止是流复制状态为streaming流复制状态显示streaming只能说明日志传输正常但备库的数据能否对外提供服务还要验证一下。先把备库切成只读模式验证SHOW transaction_read_only;备库应该显示on。然后建一张临时测试表CREATE TABLE test_replication(id int primary key, note text);这条命令在备库上会报错——备库是只读的不允许写操作。不要觉得奇怪这是正常现象说明hot_standby的读保护生效了。真正的验证是在主库写数据然后在备库查-- 主库执行 INSERT INTO test_replication VALUES (1, hello standby); -- 备库执行 SELECT * FROM test_replication;能查到这条数据并且延迟很小说明整个链路是通的主库写入→WAL生成→流复制或归档传输→备库重放→备库可查询。我一般还会顺手查一下pg_stat_replication里的replay_lsn跟主库当前pg_current_wal_lsn()的差距。如果差距一直在几MB以内说明实时性很好如果越来越大就得看网络或磁盘IO了。5. 工具选型背后的考量为什么我坚持用pg_rman5.1 pg_rman对比pg_basebackup各有各的舞台聊了这么多实操回到一个很多人问过的问题既然pg_basebackup更常见为什么还要费劲用pg_rman的备份集来搭备库这俩不是替代关系而是不同场景下的选择。pg_basebackup是PostgreSQL自带的物理备份工具它直接从主库拉数据用法简单一条命令就能生成一个备库。如果你只是偶尔搭一台备库主库负载也不高那pg_basebackup确实是最快的路径。但pg_rman有它不可替代的优势备份管理一体化pg_rman本身就是一套完整的备份系统它可以统一管理全量、增量和归档日志自动清理过期备份备份策略用一条命令就能跑。如果你已经用它做日常备份那备库只是这套系统的“副产品”零额外开销。增量备份恢复速度快不是每次都要用全量备份。如果你想在已有备份基础上搭建备库可以用pg_rman的增量备份结合归档日志恢复到任意时间点。这在数据量很大、全量备份周期长时特别有价值。不打扰主库pg_basebackup需要主库额外发送数据对主库IO和带宽有一定影响。而pg_rman的备份是独立于主库的备份时也会连接主库做在线备份但数据文件是从磁盘读的对主库的影响通常更可控。校验和恢复验证更完善pg_rman的validate和backup -C自带校验机制可以对备份集做完整性检查。这在灾难恢复场景下尤其重要——你不能等到真出事了才发现备份是坏的。5.2 备份集恢复与流复制组合的运维价值把pg_rman备份集恢复成备库这件事放在更大的运维视角下看它其实是一种“备份即服务”的实践。我认识的一些DBA团队把pg_rman备份集当成了“备库工厂”——每当需要新环境测试环境、预发布环境、分析环境不是去主库拉pg_basebackup而是直接从备份系统恢复一份。这样做的好处是主库的负载完全不受影响而且恢复出来的数据天然就是某个时间点的快照便于做数据对比和问题追溯。从备份集恢复备库还有一层好处它是验证备份可用性的最佳手段。很多团队的备份策略形同虚设因为备份完从来不恢复等到真需要恢复时才发现备份有问题。而定期从备份集搭建备库本身就是一次完整的恢复演练。你既得到了一个高可用备库又验证了备份链路一石二鸟。我在实际操作中有一个习惯每次对主库做大版本升级或重大结构变更前都会先从最新的pg_rman备份集恢复一个临时实例然后在这个临时实例上做演练。等演练通过后再把真实的备库切流升级。这个流程我不敢说万无一失但确实帮我避过好几次坑。6. 常见问题与排查技巧实录6.1 备库启动报错recovery.conf已不再支持这是PG 12以后最常见的坑。从老版本操作习惯过来的DBA习惯性地在数据目录里创建了recovery.conf然后启动时报错FATAL: using recovery command file recovery.conf is not supportedPG 12开始recovery.conf机制被移除了取而代之的是standby.signal和postgresql.conf里的参数。解决办法删掉recovery.conf创建standby.signal文件把原来写在recovery.conf里的参数比如primary_conninfo、restore_command转移到postgresql.conf里。这个报错的核心逻辑是PostgreSQL宁可直接报错也不愿意让你用旧机制启动因为它想确保你的配置方式是正确的。所以看到这个报错时不要想着改什么参数绕过直接迁移配置方式就行。6.2 备库一直catchup日志却不见增长这是另一个高频问题启动备库后pg_stat_replication里显示state catchup但replay_lsn一直不动或者增长极慢。先确认网络是否正常备库到主库的5432端口是否通。然后查看备库日志有没有报错ERROR: requested WAL segment 00000001000000000000002A has already been removed这个报错说明备库需要的WAL已经不在了。原因通常是备库停了一段时间主库的WAL早就被循环覆盖了而归档目录里也找不到对应的日志。解决办法是利用pg_rman的归档机制确保归档日志保留时间足够长。如果归档也没有那只能重新做全量备份或者用pg_basebackup重新搭了。另外还有一个容易被忽略的点restore_command是否正确。如果restore_command写错了比如路径不对、权限不对备库补日志时就会出现问题。排查时可以手动执行一下restore_command里的命令看看能不能找到对应的归档文件ls /backup/archive/00000001000000000000002A如果文件不存在说明归档链路有问题去看主库的archive_command是否正常执行pg_stat_archiver里有没有报错。6.3 备库追平后主库上却看不到备库有时备库日志显示started streaming WAL to primary但主库pg_stat_replication里就是查不到这个备库。这种情况十有八九是application_name填的和实际连接的对不上或者备库连接的是某个中间层比如连接池、代理主库看到的连接地址不对。检查思路在主库上执行SELECT * FROM pg_stat_replication;看application_name和client_addr是什么。如果application_name是空或默认值说明备库连接时没带名字。可以在备库的primary_conninfo里显式加application_namestandby01。如果主库上完全没有记录说明备库的流复制连接没有建立成功。这时候去备库日志里找原因常见的是pg_hba.conf没有放行复制连接或者密码错误。我当时排查过一个问题备库明明起来了主库也显示有复制连接但application_name全是walreceiver原因是备库配置里用了replicationdatabase之类的选项导致它走的是数据库连接而不是复制连接。后来把primary_conninfo简化只留host、port、user、password、application_name问题就解决了。6.4 主备数据不一致如何快速定位流复制备库虽然物理上复制主库但偶尔也会出现数据不一致的情况。常见原因包括主库上有未提交事务导致的数据文件不一致、备份时没有正确进入在线备份模式、插件或扩展在备库上不一致等。排查手段一般是对比主备库的系统表和数据字典比如pg_control里的系统标识符。在主备库上分别执行pg_current_wal_lsn()和pg_last_wal_replay_lsn()对比位点。查询主库的pg_stat_replication看replay_lag是否有异常。如果怀疑某张表数据不一致可以用pg_dump分别导出对比或者用工具如pg_comparator做数据比对。说实话主备数据不一致在排除了备份源问题后更多是硬件或文件系统层面的问题。如果反复出现检查一下磁盘是否满、文件系统是否损坏、内存是否有ECC错误。这些都是血泪经验。6.5 常见问题速查表我把上面提到的几个问题整理成一张速查表方便排查时直接对照。故障现象可能原因排查方向解决办法备库启动报错recovery.conf not supportedPG 12移除了recovery.conf查看日志确认版本创建standby.signal参数写入postgresql.confstatecatchup日志不增长WAL被清除或归档缺失查看备库日志、检查归档目录补齐归档或重建备库主库看不到备库连接pg_hba.conf未放行、密码错误、application_name缺失查看备库日志、主库pg_stat_replication修正pg_hba.conf检查primary_conninfo恢复后数据目录权限错误文件属主不是postgres尝试启动看报错执行chown -R postgres:postgres备库查询报只读错误备库本来就不允许写检查transaction_read_only属正常现象做只读查询即可7. 从备库到高可用后续还能怎么扩展备库起来了流复制也正常了但这只是第一步。很多团队搭完备库就以为万事大吉其实备库的运维才刚刚开始。日常巡检时我会在监控系统里盯这几个指标主库的pg_stat_replication重点看replay_lag和sent_lsn与主库pg_current_wal_lsn()的差值。备库的pg_stat_wal_receiver看status是否为streaming。主库的WAL归档目录增长情况以及pg_rman备份是否按计划执行。如果条件允许建议做主备切换演练把备库提升为主库pg_ctl promote或select pg_promote()然后让业务打到新主库上验证整个高可用链路是否真的可用。不要等到主库挂了才第一次做切换那时候发现的问题往往已经太晚了。另外流复制备库也可以承担一些实用的职责比如作为报表库或分析库把重查询扔到备库上减轻主库压力。作为备份源从备库做pg_basebackup或pg_rman备份完全不打扰主库。作为异地灾备节点如果备库地理位置与主库不同数据安全性更高。pg_rman与流复制的组合还有一个进阶玩法定时从最新的全量备份恢复一个“临时备库”然后用它做数据校验或测试。这个临时备库用完即删不占用长期资源却能持续验证备份的可用性。我的做法是在每周日凌晨跑一个自动化脚本恢复备份→启动备库→跑一组数据校验SQL→关闭并清理。这个流程看起来多花了点资源但每次备份集质量出问题都是它先发现的。所以如果你问我搭建一个流复制备库最大的收获是什么我会说不只是高可用而是整个数据链路的安全感。你明确知道自己的备份是能用的是能在关键时刻救命的这种感觉比任何监控面板上的绿灯都踏实。