2. PostgreSQL物理复制原理

PostgreSQL 的物理复制不是把 SQL 语句重新执行一遍,而是把主库上“数据页如何被修改”的物理证据——WAL(Write-Ahead Log,预写式日志)——原样传送到备库并重放。理解这条链路,是判断跨地域容灾中“能丢多少数据、切换要多久、备库是否可用”的基础。

一、WAL 的生成:先写日志,再写数据页

PostgreSQL 遵循 WAL 规则:任何数据页的修改,必须先把对应的 WAL 记录刷入持久存储,之后才允许把脏数据页写回磁盘。这样即使数据页尚未落盘就宕机,也能通过重放 WAL 恢复。

WAL 在逻辑上是一条连续的字节流,物理上被切分为固定大小的段文件,默认 16MB。每个段有唯一名称,例如 000000010000000000000001,由时间线 ID、逻辑日志号和段号组成。WAL 记录以 LSN(Log Sequence Number)标识位置,格式为“段号/段内偏移”,如 0/1A2B3C4。LSN 单调递增,是复制进度、恢复目标和一致性判断的统一坐标。

关键参数决定生成节奏:wal_level 至少为 replica 才能支撑物理复制;wal_keep_size 或复制槽决定主库为备库保留多少 WAL;max_wal_senders 限制并发发送进程数。若备库落后超过保留量,主库会回收所需 WAL,备库只能重建。

二、WAL 传输:从主库到备库的路径

WAL 传输有两条路径。第一条是 流复制:备库通过 walreceiver 进程连接主库的 walsender,主库在新 WAL 生成后近乎实时推送。第二条是 归档恢复:主库用 archive_command 把已完成的 WAL 段复制到归档目录,备库用 restore_command 拉取。生产环境通常两者并用:流复制保证低延迟,归档作为追赶和兜底。

备库收到 WAL 后,由 startup 进程重放。重放位置由 pg_stat_replication 中的 sent_lsn、write_lsn、flush_lsn、replay_lsn 四个指标刻画,分别代表主库已发送、备库已写入、备库已刷盘、备库已重放的位置。跨地域场景中,网络往返时延会直接体现在 sent_lsn 与 replay_lsn 的差距上。

PostgreSQL 物理复制:WAL 生成、传输与重放链路 主库 Primary 后端进程 Backend 执行事务,产生 WAL 记录 WAL Buffer 先写日志,再写数据页 WAL 段文件(16MB) LSN 单调递增,如 0/1A2B3C4 walsender 进程 max_wal_senders 控制并发 archive_command 归档已完成 WAL 段 备库 Standby walreceiver 进程 接收 WAL 流并写入本地 WAL 接收缓冲区 write_lsn / flush_lsn startup 进程重放 replay_lsn 持续推进 数据页 / 索引 与主库物理一致 restore_command 从归档拉取缺失 WAL 流复制协议 TCP 长连接,实时推送 归档传输 兜底与追赶 LSN 进度: sent_lsn write_lsn flush_lsn replay_lsn 差距反映网络时延与重放延迟

三、流复制协议:一次握手,持续推送

流复制基于 PostgreSQL 前端/后端协议扩展。备库启动时向主库发起连接,携带 replication=true 参数,主库校验 pg_hba.conf 中的 replication 条目后建立 walsender 会话。备库在握手中告知自己已持久化的 LSN,主库从该位置之后开始推送。

协议支持两种反馈模式:物理流复制下备库周期性发送 standby status update,包含 write_lsn、flush_lsn、apply_lsn;主库据此判断同步备库是否已确认。若备库长时间无响应,主库可依据 wal_sender_timeout 断开连接,避免 walsender 悬挂。

跨地域部署时,流复制连接会穿越高延迟链路。此时应关注 pg_stat_replication 的 write_lag、flush_lag、replay_lag 三个时间差指标,它们比单纯看 LSN 差值更能反映“备库落后主库多少秒”。

四、同步与异步复制模式

复制模式由主库 synchronous_standby_names 和 synchronous_commit 共同决定。

  • 异步复制:主库提交事务时不等备库确认,立即向客户端返回成功。优点是写入延迟低;缺点是主库故障时,尚未传输的 WAL 会丢失,RPO 大于 0。
  • 同步复制:主库提交事务后,必须等待指定备库将 WAL 刷入持久存储(synchronous_commit=on)或至少写入(=remote_write),才向客户端返回成功。RPO 可降为 0,但写入延迟受备库和网络影响。
  • 法定人数同步:synchronous_standby_names = 'ANY 1 (standby1, standby2)' 表示两个备库中任意一个确认即可提交,兼顾可用性与数据安全。

同步复制并非“所有备库都同步”。只有被 synchronous_standby_names 列出的备库才参与确认;未列出的备库仍以异步方式追赶。若同步备库全部不可用,主库会阻塞提交,直到 synchronous_standby_names 被调整或备库恢复。这是跨地域容灾中必须提前设计的降级路径。

跨地域场景建议:同城备库配置为同步,异地备库配置为异步或 remote_write。这样既保证同城 RPO=0,又避免异地网络抖动拖垮主库写入。

五、观察与判断:用指标验证复制状态

在主库执行以下查询,可同时看到发送与确认进度:

SELECT application_name,
       client_addr,
       state,
       sent_lsn,
       write_lsn,
       flush_lsn,
       replay_lsn,
       write_lag,
       flush_lag,
       replay_lag,
       sync_state
FROM pg_stat_replication;

判断要点:state 应为 streaming;sync_state 为 sync 表示该备库参与同步确认;replay_lag 持续增大说明备库重放跟不上;若 sent_lsn 与 replay_lsn 差距长期不收敛,需检查网络带宽、备库 I/O 或 max_wal_size 是否过小导致频繁检查点。

在备库执行 SELECT pg_last_wal_receive_lsn(), pg_last_wal_replay_lsn(); 可确认接收与重放位置。若两者相等且持续前进,说明备库已追平主库;若接收位置远大于重放位置,说明 WAL 已到但重放慢,通常与备库磁盘或 CPU 有关。

六、跨地域容灾中的取舍

物理复制的本质是“WAL 字节流的搬运与重放”。跨地域时,网络 RTT 直接放大同步复制的提交延迟,因此需要按业务 RPO/RTO 选择模式:金融交易类可接受同城同步、异地异步;日志分析类可全异步,优先吞吐。无论哪种模式,都必须保证 WAL 保留量足够覆盖最长网络中断时间,否则备库将因 WAL 被回收而无法追赶,只能重新搭建。

理解 WAL 生成、流复制协议和同步/异步模式,才能在后续的故障切换设计中,准确判断“切换时丢不丢数据、备库能不能立刻接管、主库恢复后如何重新加入”。