Dimitri Fontaine:超越单写入者的 Postgres 逻辑复制

Dimitri Fontaine:超越单写入者的 Postgres 逻辑复制

💡 原文英文,约6100词,阅读约需22分钟。
📝

内容提要

文章探讨用 PostgreSQL 19 与 pg_auto_failover 构建多区域多写入系统。逻辑复制已支持行过滤、防循环、故障转移槽、序列同步和冲突计数;实测主库故障后数据不丢不重,新区域可无停机加入。但逻辑复制仍不复制 DDL、不解决冲突、不跨写入者分配唯一 ID,需借助短暂停、pglogical 或 pgcopydb,以及按区域划分主键来规避。

🔎

延伸解读

故障转移槽的隐性前提

文章实测表明,故障转移槽能保证主库故障后消费者不丢行,但前提是槽创建时指定 failover = true,且 synchronized_standby_slots 必须列出将被提升的备库。若漏掉后者,消费者可能多出 69 到 92 行新主库从未有过的数据,并因此错过 3 到 13 行。这些条件缺失时不会报错,只会静默失效,因此部署时需逐项核对。

按区域划分主键的代价与收益

为避免多写入者冲突,文章建议将区域作为主键的一部分,例如 (region, event_id)。这样每个区域可独立使用本地序列,无需全局序列分配,复制时也无需冲突解决规则。代价仅是主键变宽,但换来的是构造上无冲突,以及故障转移后数据不重不丢。文章强调,这是目前核心 Postgres 唯一内置的答案。

DDL 复制仍是短板

核心逻辑复制不复制 DDL,加列时订阅端会因缺少列而报错并重试。文章测试了三种加列方式:先改发布端再改订阅端会导致订阅端滞后;同时改两端在低负载下可行但窗口极短;暂停写入、排空复制、再改所有主库最稳妥,五区域实测仅阻塞 0.9 到 1.5 秒。若不能暂停,可用 pglogical 或 pgcopydb 将 DDL 放入流中,但各有局限。

新增区域与长距离复制的注意点

文章演示了用五条语句无停机加入第五个区域,首次拷贝 60 万行仅需 2 秒,写入者等待不超过 0.2 秒。但作者提醒,大表长拷贝会让源库保留大量 WAL,例如每秒 3 MB 时十小时拷贝约保留 100 GB,需关注 max_slot_wal_keep_size 和槽状态。跨区域链路是异步的,距离体现为延迟,而非写入阻塞。

❓

Q&A

用 PostgreSQL 19 和 pg_auto_failover 构建多区域多写入系统,主库故障时数据会丢吗?

不会丢。实测主库被 kill 后,所有已确认的行都存活,每个区域最终持有相同的行,没有缺失也没有重复。这依赖于 failover slot 和 synchronized_standby_slots 的正确配置。

PostgreSQL 逻辑复制现在能自动同步 DDL 吗?如果不能,有什么替代方案?

核心逻辑复制不复制 DDL。表结构变更需要短暂暂停写入,或者使用 pglogical 的 replicate_ddl_command() 将 DDL 放入流中,或者使用 pgcopydb 的事件触发器捕获 DDL。但 pglogical 在双方都写入时可能不够,pgcopydb 仅支持单向链接。

如何避免多个写入者之间的冲突?

通过设计避免冲突:每个区域拥有自己的行,主键包含区域标识(如 (region, event_id)),这样任何行只能由一个区域写入。复制时使用行过滤,只发送对方缺少的行。这样就不需要冲突解决机制。

如何向运行中的多区域系统添加一个新区域?

通过五条语句即可:在新区域创建相同的表,在相邻区域创建发布(过滤掉新区域的行),在新区域创建发布(只发布自己的行),然后建立双向订阅(使用 failover = true 和 copy_data = false)。实测新区域在 8-11 秒内启动,首次拷贝 60 万行仅需 2 秒,写入者无感知。

逻辑复制中序列是如何同步的?PostgreSQL 19 有什么改进?

PostgreSQL 19 支持 CREATE PUBLICATION ... FOR ALL SEQUENCES 和 ALTER SUBSCRIPTION ... REFRESH SEQUENCES,可以按需复制序列值。但序列不会实时流式复制,刷新后订阅端的序列会更新到发布端的当前值,适用于切换场景,但不能让多个写入者共享同一个序列。

与 BDR 相比,当前基于核心逻辑复制的方案有哪些不足?

主要不足包括:不复制 DDL(需要暂停或外部工具),不解决冲突(仅计数),不跨写入者分配唯一 ID(需按区域划分主键),以及消费者在故障转移后需要自行检查是否超前(如使用 IDENTIFY_SYSTEM)。BDR 提供了全局锁、多数投票序列等协调机制,但代价较高。

🏷️

标签

➡️

继续阅读