本节摘要:物理流复制传输原始 WAL 字节流,备库按字节重放,与主库逐块一致,适合高可用与读写分离;逻辑复制先由发布端把 WAL 解码成"INSERT/UPDATE/DELETE"级别的逻辑变更再传输,订阅端自行执行,可跨大版本、可挑表、可双向。两套体系吃的是同一条日志流,服务的是不同的问题。
主库启动 WAL 发送进程,备库以 WAL 接收进程连上来,日志字节流实时推送,备库持续按 WAL 重放——本质上是永不停止的崩溃恢复。
-- 备库侧的连接配置示意 -- primary_conninfo = '主库地址与复制账号' -- 备库查询复制延迟 SELECT now() - pg_last_xact_replay_timestamp() AS lag;
三种角色由参数决定:
| 模式 | 行为 | 典型用途 |
|---|---|---|
| 异步流复制 | 日志发出即算完,不等备库 | 读写分离、容灾 |
| 同步复制 | 主库提交要等至少一个备库落盘 | 零丢失要求 |
| 级联复制 | 备库再向下游分发 | 多机房减少主库负担 |
代价是整个集群共享一个世界观:备库与主库物理一致,所以备库只读、不能跨大版本、也不能只要其中几张表。
逻辑复用在 WAL 上加了一层"逻辑解码":发布端的解码插件读出 WAL,还原成行级变更事件,按发布定义过滤后传给订阅端,订阅端像执行普通 SQL 一样应用它们。
-- 发布端:只发布两张表 CREATE PUBLICATION sales_pub FOR TABLE orders, order_item; -- 订阅端(可以是另一套库、另一个大版本) CREATE SUBSCRIPTION sales_sub CONNECTION '发布端连接串' PUBLICATION sales_pub;
因为传输的是逻辑变更而非页面字节,约束彻底改变:
| 需求 | 选谁 | 原因 |
|---|---|---|
| 高可用、自动故障切换 | 物理流复制 | 逐块一致,切换零解释成本 |
| 跨版本滚动升级 | 逻辑复制 | 字节格式随版本变化,逻辑层免疫 |
| 只同步部分业务表到报表库 | 逻辑复制 | 物理复制是全有或全无 |
| 多主/双向写入 | 逻辑复制(谨慎设计) | 物理复制不支持两方向 |
💡 关键直觉:物理复制的单位是"页面怎么变",逻辑复制的单位是"这行数据怎么变"。前者的世界必须一模一样,后者的世界只需要表结构能对上。
物理复制的账本由复制槽管理。备库断线期间,主库必须保留未发送的 WAL——复制槽就是这份"欠条"的登记处:
-- 主库上看槽位与积压 SELECT slot_name, active, restart_lsn, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained FROM pg_replication_slots;
slot_name | active | restart_lsn | retained -----------+--------+-------------+---------- standby1 | t | 0/4A100000 | 12 MB
retained 列是关键读数:备库健康时它就是正常延迟;备库长时间下线而槽未删,它会无限增长,直到主库日志盘被占满、整库停摆——这是流复制最经典的自杀路径。配套的保险丝是 max_slot_wal_keep_size:给每个槽设保留上限,超限后废弃该槽的保留承诺(代价是依赖它的备库需要重建)。生产配置应当显式设置这个上限,而不是赌备库永远健康。
复制延迟不是一个数,是三个。发送延迟(主库已写、未发)、落盘延迟(备库已收、未刷)、重放延迟(备库已刷、未应用),分别对应 pg_stat_replication 的 write_lag、flush_lag、replay_lag:
SELECT application_name, state, sync_state, write_lag, flush_lag, replay_lag FROM pg_stat_replication;
区分它们在排障时有实际意义:发送延迟大查网络与主库负载;落盘延迟大查备库磁盘;重放延迟大查备库上的重放单线程瓶颈(大事务在备库重放时无法并行,主库跑十分钟的批量更新,备库也要重放约十分钟)。读写分离的业务读到的是重放完成的数据,因此对一致性敏感的读必须以 replay_lag 为准,必要时用同步级别或应用层粘主库解决。
暗礁一:初始数据同步期间发布端持锁。CREATE SUBSCRIPTION 默认先做一次全量拷贝,大表拷贝期间会在发布端拿表锁,生产表上要错峰或用 schema 与数据分步的方式做。暗礁二:约束冲突即停。订阅端应用变更遇到唯一键冲突,复制立刻停摆等人工介入——订阅端表如果有业务同时在写,冲突几乎必然发生,所以订阅端表通常要对业务侧禁写。暗礁三:大事务同步放大。发布端一个千万行的大事务,解码后作为单事务传给订阅端,订阅端整事务应用;主库跑两小时的批量任务,订阅端可能跟随卡两小时。批量迁移类任务在逻辑复制链路上要拆小事务提交。
这三条暗礁共同指向一个结论:逻辑复制是灵活的数据分发工具,不是即插即用的镜像;它的每一分灵活性都以"两端各自承担工程责任"为代价。