首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >PG 同步逻辑复制有巨坑

PG 同步逻辑复制有巨坑

作者头像
用户4035096
发布2026-07-09 20:38:43
发布2026-07-09 20:38:43
90
举报

PG 同步逻辑复制有巨坑

近期用AI解读的2个补丁出了点小插曲, 给哼哼打脸了, 可能是AI未读取代码, 而是直接从comment推演的. 还好传成老师和zhijie帮忙点出了错误.

  • 《PostgreSQL 19 preview - 再次提高逻辑复制可靠性》
  • 《PostgreSQL 19 preview - 有slot同步时强制 promote》

今天想综合这几天的信息, 纠正一下错误同时指出PG 逻辑复制的一个巨坑.

首先来回顾一下什么是同步复制槽.

同步复制槽目的就是保证使用物理流复制从库时, 如果发生failover(激活从库成为新主库), 这个同步复制槽的消费者漂移到新激活的主库上时, 不会丢数据.

基于这个前提条件, 以某个同步复制槽为例, 消费者在老的主库上消费的最后的WAL位点, 此前的所有WAL必须已经同步给从库.

PG 19 推出了一个函数 pg_sync_replication_slots() . 类似设置了一个栅栏, 在从库执行它时, 这个函数返回true之前, 必须等待此函数调用前就已经在老主库上存在的所有同步复制槽, 这些槽的已消费的WAL(如果有多个同步复制槽, 则取最小的那个wal), 此位置之前的所有WAL都已经同步到当前从库.

但是这个逻辑还是有个小问题, 因为这个操作是异步的, 你在从库看到的永远是主库过去的slot状态, 所以消费者在主库不断消费, 你看到的wal永远比实际情况更早(旧).

因此补丁不是保证在从库激活成新的主库后不丢wal, 而是保证它不删消费者需要的wal. 大不了多消费(消费层面要做好幂等), 但绝对不能少.

不丢WAL是另一个逻辑保证的, 也就是PG 很早的版本就已经存在的同步流复制(quarum based sync replication). 不过这个也有漏洞, 当同步出现堵塞时, 它只是阻止commit返回给客户端, 不阻止本地wal持久化, 所以结果是客户端感觉还没提交(因为一直没有收到返回), 实际上这个事务的wal已经在本地持久化. 在这个情况下, 主库上的消费者是可以消费这个wal的.

这种情况下, 逻辑消费者可能比物理的从库同步了更多的信息. 所以 pg_sync_replication_slots() 在这个场景下也有作用, 它可以将slot标记为未同步.

PG 的巨坑来自堵塞, 而不是丢数据, 请往下看

同步复制槽的功能开启有点复杂:

参考文档 https://www.postgresql.org/docs/current/logicaldecoding-explanation.html

For the synchronization to work, it is mandatory to have a physical replication slot between the primary and the standby (i.e., primary_slot_name should be configured on the standby), and hot_standby_feedback must be enabled on the standby. It is also necessary to specify a valid dbname in the primary_conninfo. It's highly recommended that the said physical replication slot is named in synchronized_standby_slots list on the primary, to prevent the subscriber from consuming changes faster than the hot standby. Even when correctly configured, some latency is expected when sending changes to logical subscribers due to the waiting on slots named in synchronized_standby_slots. When synchronized_standby_slots is utilized, the primary server will not completely shut down until the corresponding standbys, associated with the physical replication slots specified in synchronized_standby_slots, have confirmed receiving the WAL up to the latest flushed position on the primary server.

  • 首先需要找一个物理standby, 把它当成同步复制节点(我说的是逻辑复制层面的同步, 因为它只影响逻辑订阅)
  • 然后要把这个物理standby节点连接主节点用到的物理复制槽配置到主节点的 synchronized_standby_slots 里面
  • 这个物理standby节点要开启 hot_standby_feedback (开启这个的副作用聊过, 意味着这个从节点的长事务可能导致主库的膨胀、CPU/IO标高(vacuum 无用功造成的))
  • 最后, 主节点如果要停库, 它需要等待这个物理standby已完全同步主节点最后的WAL flushed position

当然, 这是PG 18的情况, 用可用性换取切换后逻辑订阅同步复制槽的可靠性. 19也许会有改观. 例如 是不是不用配置单一的物理standby来保障逻辑同步复制槽的可靠性, 而是直接和quorum base sync standby结合起来呢?

下面是详细的同步逻辑复制槽配置详解, 以及摆在那里的坑

以下依旧是AI生成, 不过这次AI读了代码, 不是只看comment哦.


同步逻辑复制槽配置详解

PostgreSQL的逻辑复制槽同步功能允许在主备切换后继续逻辑复制。以下是详细的配置说明:

核心配置参数

主服务器配置

synchronized_standby_slots - 指定逻辑WAL发送进程需要等待的物理复制槽列表 :

代码语言:javascript
复制
-- 在postgresql.conf中设置    
synchronized_standby_slots = 'sb1_slot,sb2_slot'    

这确保逻辑复制不会比物理备机更快地消费变更,防止数据丢失。

备服务器配置

sync_replication_slots - 启用物理备机从主服务器同步逻辑故障转移复制槽 :

代码语言:javascript
复制
-- 在postgresql.conf中设置    
sync_replication_slots = on    

必要的前置条件

根据ValidateSlotSyncParams()函数,同步复制槽需要满足以下条件:

  1. wal_level >= logical - 必须启用逻辑复制级别
  2. primary_slot_name - 必须配置物理复制槽
  3. hot_standby_feedback = on - 必须启用热备反馈
  4. primary_conninfo - 必须配置主服务器连接信息

工作原理

自动同步机制

启用sync_replication_slots后,备机会启动槽同步工作进程(ReplSlotSyncWorkerMain),定期从主服务器获取故障转移槽信息并同步到本地 。

手动同步

可以使用pg_sync_replication_slots()函数手动触发同步 :

代码语言:javascript
复制
SELECT pg_sync_replication_slots();    

槽状态管理

同步的槽可能处于两种状态 :

  • RS_TEMPORARY - 临时状态,WAL不可用或无法构建一致快照
  • RS_PERSISTENT - 持久状态,可用于故障转移后的逻辑复制

实际配置示例

主服务器配置

代码语言:javascript
复制
-- 创建物理复制槽    
SELECT pg_create_physical_replication_slot('sb1_slot');    
    
-- 创建逻辑故障转移槽    
SELECT pg_create_logical_replication_slot('logical_slot', 'pgoutput',     
                                          false, false, true);    
    
-- 配置同步槽列表    
ALTER SYSTEM SET synchronized_standby_slots = 'sb1_slot';    
SELECT pg_reload_conf();    

备服务器配置

代码语言:javascript
复制
-- postgres.conf    
primary_conninfo = 'host=primary port=5432 dbname=postgres'    
primary_slot_name = 'sb1_slot'    
hot_standby_feedback = on    
sync_replication_slots = on    

验证同步状态

检查备机上的同步槽状态 :

代码语言:javascript
复制
SELECT slot_name, synced, temporary, invalidation_reason     
FROM pg_replication_slots     
WHERE slot_name IN ('logical_slot');    

注意事项

  1. WAL保留要求 - 同步槽需要主服务器保留足够的WAL和系统目录行
  2. 级联备机限制 - 不支持从级联备机同步复制槽
  3. 临时槽使用限制 - 临时同步槽不能用于逻辑解码

Notes

  • 同步复制槽功能主要用于高可用场景,确保主备切换后逻辑复制能够无缝继续
  • 配置时需要确保网络延迟和WAL保留策略能够满足同步要求
  • 建议在生产环境部署前进行充分的故障转移测试

当 synchronized_standby_slots 配置了多个slot时, 同步复制槽要等待synchronized_standby_slots里面的所有slot都到达同步位置还是任意一个?

synchronized_standby_slots 配置了多个slot时,同步复制槽需要等待所有slot都到达同步位置,而不是任意一个。

详细说明

等待机制

StandbySlotsHaveCaughtup() 函数中,系统会遍历 synchronized_standby_slots 中配置的所有物理复制槽:

代码语言:javascript
复制
name = synchronized_standby_slots_config->slot_names;    
for (int i = 0; i < synchronized_standby_slots_config->nslotnames; i++)    

对于每个slot,系统检查其 restart_lsn 是否已经达到等待的位置:

判断逻辑

只有当所有配置的slot都满足条件时,函数才返回 true

代码语言:javascript
复制
if (caught_up_slot_num != synchronized_standby_slots_config->nslotnames)    
    return false;    

这意味着:

  • caught_up_slot_num 记录已赶上进度的slot数量
  • 只有当这个数量等于配置的slot总数时,才认为所有slot都已同步

实际影响

这种设计确保了逻辑复制不会比任何一个物理备机更快地消费变更,从而防止在主备切换时发生数据丢失。

Notes

  • 如果其中任何一个slot不存在、被无效化或未激活,逻辑复制将被阻塞
  • 这种"等待所有"的策略是为了保证数据一致性,确保所有备机都接收到变更后逻辑复制才能继续

为什么要牺牲可用性换取可靠性呢?

为什么同步槽等待列表支持配置多个slot, 但确是等待全部节点, 而不是使用quorum base sync standby那套逻辑呢?

你怎么看? 欢迎留言.

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2025-12-22,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 digoal德哥 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • PG 同步逻辑复制有巨坑
  • 同步逻辑复制槽配置详解
    • 核心配置参数
      • 主服务器配置
      • 备服务器配置
    • 必要的前置条件
    • 工作原理
      • 自动同步机制
      • 手动同步
      • 槽状态管理
    • 实际配置示例
      • 主服务器配置
      • 备服务器配置
    • 验证同步状态
    • 注意事项
    • Notes
    • 当 synchronized_standby_slots 配置了多个slot时, 同步复制槽要等待synchronized_standby_slots里面的所有slot都到达同步位置还是任意一个?
    • 详细说明
      • 等待机制
      • 判断逻辑
      • 实际影响
    • Notes
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档