西西软件园多重安全检测下载网站、值得信赖的软件下载站!
软件
软件
文章
搜索

首页 → 业内动态 → 业内资讯 → PG物理复制和逻辑复制差在哪:一条WAL走完全程

PG物理复制和逻辑复制差在哪:一条WAL走完全程

相关软件相关文章发表评论 来源:本站整理时间:2026/11/3 7:30:22字体大小:A-A+

作者:xiix点击:0次评论:0次标签: PostgreSQL流复制 物理复制 逻辑复制 复制槽 WAL日志

  • 类型:数据库类大小:352.5M语言:中文 评分:10.0
  • 标签:
立即下载

去年我们给一个客户做数据库高可用改造,主库在一台物理机上,客户问得很直接:PostgreSQL 复制是不是就是「把数据同步到另一台机器上」?我说是,但这句话没说清最关键的一点同步过去的是「文件」还是「数据变更」。这一条路走的是物理复制,换一台机器、换个大版本、跨个机房,能用的办法全不一样;另一条路走的是逻辑复制,可以只搬某几张表、只搬某些字段,甚至做跨大版本的迁移。选错了,后面全是补丁。

如果你要给数据库主从准备机器,磁盘的 IOPS 和主库持平往往比 CPU 更容易被忽略,下面两家在存储配置上做得比较扎实,可以先看看规格再决定:hosteons(纯 SSD RAID10 KVM VPS,主机托管与独立服务器),onidel(全球 NVMe SSD 云服务器)

先说结论:两种复制解决的不是同一个问题

物理复制(也常叫流复制、Streaming Replication)走的是 PostgreSQL 的 WAL 机制。主库把每一次数据变更先写进 WAL 文件,然后把这批 WAL 通过网络传给备库,备库用 pg_receivewal 或者 standby 模式把它重放一遍。它复制的是数据目录的字节流,所以备库必须和主库是同一个大版本、同一套 CPU 架构、而且几乎所有文件都要一模一样。

逻辑复制走的是另一套机制。主库把变更解析成 SQL 语句(INSERT、UPDATE、DELETE)或者行数据(带 REPLICA IDENTITY 的整行数据),通过一个复制连接推给订阅端,订阅端在自己的库里执行。它传的是「数据怎么变的」,不是「文件长什么样」。

PostgreSQL复制槽与流复制原理概念图

这个区别带来的直接后果是:物理复制能保证备库和主库在字节层面一致,逻辑复制则可能出现两边行数对得上、内容却因为触发器、默认值、序列这些细节产生细微偏差的情况。

物理复制:省心,但主从必须同版本

搭物理复制最常见的方式是 pg_basebackup,一条命令把主库数据目录整个拉过来,再在配置里写上 primary_conninfo 和 standby.signal,备库就起来了。版本这块没什么好商量的:PostgreSQL 只支持从大版本往小版本复制,比如 16 可以复制到 15,反过来不行。所以做升级的时候,常规做法是先用逻辑复制把数据搬到新大版本上,物理复制留着做日常的容灾副本。

物理复制真正的坑不在搭,而在之后。有一个我们踩得很典型的:客户的一个只读查询接口连着备库,某天备库磁盘满了,连接被踢掉。我们到现场一看,备库恢复了,但主库的磁盘也快满了pg_wal 目录里堆了几十 GB 的 WAL 文件。主库把 WAL 传给备库之后,本以为可以删掉了,但因为备库掉线时没能及时清理,这些 WAL 全被主库保留着。

这就是复制槽(replication slot)的作用和代价。复制槽会在主库上占一个位置,告诉主库「这个备库需要的 WAL 你先别删,等它来取」。这个设计本身是对的,它能防止备库短暂断连时需要的 WAL 被自动回收掉。但如果你创建了复制槽之后备库再也没连上来,或者备库被误删了,这个槽就会一直挂着,主库上的 WAL 无限堆积,直到磁盘写满、主库彻底不可用。

WAL日志文件堆积导致服务器磁盘占满

排查这类问题其实就一条命令,在主库上执行:

SELECT slot_name, active, restart_lsn, wal_status, pg_size_pretty(pg_wal_lsn_diff(pg_current_wal_lsn(), restart_lsn)) AS retained FROM pg_replication_slots;

如果 active 是 false,而 retained 那一列是个很大的数字,基本就锁定了。处理方式有两种:槽确实还要用,就去把备库连上让它把 WAL 拉走;槽已经废弃了,直接 SELECT pg_drop_replication_slot('slot_name'); 掉。日常监控里,我建议把这个 retained 大小做成一个告警项,超过几个 G 就报警,别等磁盘满了才发现。

逻辑复制:灵活,但要自己处理一致性

逻辑复制的价值在两个场景里体现得特别明显。一个是跨大版本的迁移,PostgreSQL 13 到 17 这种跨度,物理复制做不了,只能靠逻辑复制把数据搬过去。另一个是「只同步一部分数据」,比如主库里存了十张业务表,但备库只需要其中三张做报表查询,那就在发布端上建一个 PUBLICATION 只发布这三张表,订阅端建 SUBSCRIPTION 订阅,链路上的数据量会小很多。

逻辑复制的代价是它不复制 DDL。主库上 ALTER TABLE 加个字段,发布端不会自动同步到订阅端,得手动执行一遍。主键也是关键UPDATE 和 DELETE 在订阅端要定位到具体行,默认靠主键,主表没有主键的话就必须配置 REPLICA IDENTITY,用全列做匹配,那性能会明显掉一截。

还有一点容易忽略:逻辑复制的延迟是「数据到订阅端」的延迟,不是「事务提交到」的延迟。真要判断复制是否健康,别看主库端的发送状态,去订阅端看 pg_stat_subscription 里的 latest_end_lsn 和 received_lsn 差值,那个才是真正的端到端延迟。

运维工程师在控制台检查数据库复制状态

我们现在的落地做法

综合下来,我们在客户环境里用的是这套组合:同机房一台物理机做物理复制备库,用复制槽保证断连不丢 WAL,主备之间跑千兆内网,专为复制流量做限速和监控;同时用逻辑复制把核心的三张表同步到另一台机器做只读报表,避开主库的查询压力。跨机房那条链路走的是专线,因为流复制对延迟比较敏感,抖动一次延迟就往上跳。

另外几个细节值得单独提:一是 wal_level 必须是 replica 以上(要逻辑复制就得更改),改完要重启才生效;二是 max_wal_senders 和 max_replication_slots 要留够,复制槽数量不能超,否则备库连不上;三是备库一定要配 hot_standby = on 才能对外提供只读;四是最容易被忽略的,主库上要开 archive_mode 配合 archive_command,WAL 归档是最后一道保险,物理复制挂掉时可以从归档重做备库。

两台服务器通过网络专线同步数据库数据

最后提一句选型上的经验:如果你只是想要一个「主机挂了能立刻顶上」的备库,别折腾逻辑复制,物理复制更简单也更可靠;如果你需要跨版本、跨表、跨字段,甚至只搬一部分数据,那逻辑复制是唯一选择。搞清楚了要搬的是文件还是数据,后面的参数配置基本就是照着文档抄了。

    相关评论

    阅读本文后您有什么感想? 已有人给出评价!

    • 8 喜欢喜欢
    • 3 顶顶
    • 1 难过难过
    • 5 囧囧
    • 3 围观围观
    • 2 无聊无聊

    发表评论

    昵称:
    表情: 高兴 可 汗 我不要 害羞 好 下下下 送花 屎 亲亲