逻辑复制支持同步系统表概要设计


难度 中等

下面给你一份基于 PG18 的“逻辑复制支持系统表同步”完整设计文档(可直接用于研发/评审)。我会严格结合你前面分析的源码链路来设计,重点保证:

  • 不破坏 PostgreSQL 现有逻辑复制架构
  • 可控扩展(白名单机制)
  • 具备产品化落地能力

一、背景与目标

1.1 背景

在 PostgreSQL 18 中:

  • 逻辑复制默认仅支持:

    • 普通表(heap table)
    • 分区表
    • sequence
  • 系统表(catalog)被严格排除

排除路径包括:

  1. WAL 层:RelationIsLogicallyLogged() 排除 catalog
  2. publication 层:is_publishable_relation() 排除 catalog
  3. pgoutput 层:过滤不可发布 relation
  4. 订阅端:仅允许普通表作为 apply 目标

1.2 问题

当前机制导致:

  • 无法同步:

    • schema 元信息
    • 用户自定义 catalog-like 表
    • 扩展系统表
  • 无法实现:

    • DDL 自动复制(你前面设计的核心目标)
    • metadata replication
    • cluster-level config sync

1.3 目标

实现:

支持指定系统表通过逻辑复制同步到订阅端

并满足:

  • 不影响现有逻辑复制
  • 不默认开启(必须显式控制)
  • 不破坏 catalog 一致性
  • 支持未来 DDL replication 扩展

二、总体设计

2.1 核心思路

采用:

“系统表白名单 + 分层放开”机制

而不是:

❌ 全量放开 system catalog


2.2 架构扩展点

WAL生成
   ↓
Logical Decoding
   ↓
pgoutput(发布端)
   ↓
Logical Replication Protocol
   ↓
apply worker(订阅端)

改造点:

是否修改 说明
WAL/logical logging 允许系统表产生 logical 信息
decoding ⚠️ 复用现有
pgoutput 放行系统表
protocol 不改
apply 支持系统表目标

三、核心设计

3.1 系统表白名单机制(核心)

新增统一判断函数:

bool IsLogicalRepSystemCatalog(Oid relid);

实现方式(第一版)

static bool
IsLogicalRepSystemCatalog(Oid relid)
{
    switch (relid)
    {
        case MyCatalogOid:
        case MySecondCatalogOid:
            return true;
        default:
            return false;
    }
}

3.2 GUC 支持(建议)

新增参数:

logical_replication_system_catalogs = 'pg_my_catalog,pg_xxx'

解析为 OID list。


四、发布端设计

4.1 修改 WAL 逻辑

文件:

src/include/utils/rel.h

修改点

原逻辑:

#define RelationIsLogicallyLogged(relation) \
    (!IsCatalogRelation(relation) && ...)

修改为:

#define RelationIsLogicallyLogged(relation) \
    ((!IsCatalogRelation(relation) || \
      IsLogicalRepSystemCatalog(RelationGetRelid(relation))) && ...)

4.2 publication 层改造

文件:

src/backend/catalog/pg_publication.c

4.2.1 check_publication_add_relation

原:

if (IsCatalogRelation(rel))
    ERROR

改:

if (IsCatalogRelation(rel) &&
    !IsLogicalRepSystemCatalog(RelationGetRelid(rel)))
    ERROR

4.2.2 is_publishable_class

原:

!IsCatalogRelationOid(relid)

改:

(!IsCatalogRelationOid(relid) ||
 IsLogicalRepSystemCatalog(relid))

4.2.3 FirstNormalObjectId 限制

原:

relid >= FirstNormalObjectId

问题:

  • system table OID < FirstNormalObjectId

改:

(relid >= FirstNormalObjectId ||
 IsLogicalRepSystemCatalog(relid))

4.3 schema publication 限制

禁止:

FOR TABLES IN SCHEMA pg_catalog

原因:

  • 风险太高

五、pgoutput 层设计

文件:

src/backend/replication/pgoutput/pgoutput.c

5.1 放行系统表

无需直接改 pgoutput_change:

if (!is_publishable_relation(relation))
    return;

因为我们已经修改:

is_publishable_relation()

5.2 限制能力(必须)

对于系统表:

禁用:

  • row filter
  • column list
  • generated columns

在:

get_rel_sync_entry()

中增加:

if (IsLogicalRepSystemCatalog(relid))
{
    entry->rowfilter = NULL;
    entry->columns = NULL;
}

5.3 协议不修改

继续使用:

  • RELATION message
  • INSERT/UPDATE/DELETE

六、订阅端设计

6.1 relation mapping

文件:

src/backend/replication/logical/relation.c

修改 logicalrep_rel_open

允许:

system catalog relation

6.2 apply 限制

文件:

execReplication.c

修改 CheckSubscriptionRelkind

原:

only RELATION / PARTITIONED / SEQUENCE

改:

if (IsCatalogRelation(localrel) &&
    !IsLogicalRepSystemCatalog(RelationGetRelid(localrel)))
    ERROR;

6.3 写入路径

继续使用:

  • heap_insert
  • heap_update
  • heap_delete

⚠️ 限制

必须要求:

条件 必须
有 replica identity
有唯一键
不涉及 shared catalog

七、功能限制(必须)

7.1 不支持

功能 状态
shared catalog
initial copy
DDL replication
schema 自动包含
TRUNCATE ⚠️

7.2 推荐支持对象

仅支持:

  • 用户扩展 catalog 表
  • metadata 表(类似 pglogical)

八、风险分析

8.1 最大风险

1. catalog 语义破坏

系统表不仅是数据:

  • 依赖关系
  • cache invalidation
  • OID 分配

2. apply 副作用缺失

普通 DML 不触发:

  • catalog rebuild
  • dependency update

3. shared catalog

跨数据库共享:

  • 逻辑复制无法正确处理

九、测试设计

9.1 基本测试

  • INSERT / UPDATE / DELETE
  • 多事务
  • 并发

9.2 边界测试

  • rollback
  • crash recovery
  • slot 重放

9.3 兼容性测试

  • 非系统表不受影响
  • publication 行为不变

十、未来扩展

10.1 DDL replication

该机制可直接支撑:

  • CREATE TABLE
  • ALTER TABLE
  • DROP

(通过系统表变化传播)


10.2 pg_publication_sync 结合

你前面设计的:

  • pg_publication_sync 表
  • LogLogicalDDLMessage

可以复用本机制:

👉 统一 DDL + catalog replication


十一、总结

核心改动一句话总结

在 PG18 中,通过“白名单方式”打通 WAL → publication → pgoutput → apply 四层,让指定系统表具备逻辑复制能力。


最关键的4个改动点

  1. RelationIsLogicallyLogged(WAL层)
  2. is_publishable_relation(publication层)
  3. pgoutput_change(发送层)
  4. CheckSubscriptionRelkind(apply层)

推荐落地策略

👉 不要做:

  • 全量 system table 复制

👉 应该做:

  • 只支持 1~2 张系统表
  • 或:
  • 设计专用 catalog 表(推荐)

实现步骤

下面给你一版 基于 PG18、接近可编译的补丁级设计方案。这版不是“完整可直接提交社区”的 patch,而是 面向你当前原型开发 的最小闭环实现:只允许白名单系统表参与逻辑复制,不支持 shared catalog、schema 级自动包含、initial copy、row filter、column list。现有 PG18 源码里,系统表之所以被挡住,确实主要在这几处:RelationIsLogicallyLogged() 排除 catalog;check_publication_add_relation()is_publishable_class() 排除 system table / 低 OID 对象;pgoutput_change() 再次按 is_publishable_relation() 过滤;订阅端 CheckSubscriptionRelkind() 只接受普通表、分区表和 sequence;官方文档也明确写了“Only regular tables may be the target of replication.”。(GitHub)


一、目标与边界

1.1 目标

实现一个最小可运行版本:

  • 指定白名单系统表可加入 publication
  • 发布端能把该系统表的 DML 变更写入逻辑复制流
  • 订阅端能把该系统表作为 apply 目标表
  • 不影响普通逻辑复制

1.2 明确不做

第一版不做这些,否则复杂度会急剧上升:

  • shared catalog
  • FOR TABLES IN SCHEMA pg_catalog
  • initial copy
  • row filter
  • column list
  • DDL 同步
  • 任意系统表全开放

这几个限制是必要的,因为 PG18 当前 publication / subscriber 设计就是面向普通表,尤其订阅端文档和代码都明确把目标对象限定在 regular table 语义附近。(GitHub)


二、实现策略

核心思路只有一句话:

不要改成“系统表都可复制”,而是增加一个“逻辑复制系统表白名单”机制。

建议新增一个统一 helper:

bool IsLogicalRepSystemRelationOid(Oid relid);
bool IsLogicalRepSystemRelation(Relation rel);

第一版直接写死白名单,后续再扩成 GUC 或 catalog 参数。


三、建议新增文件

建议新增一个公共头文件和一个实现文件,避免把白名单逻辑散落在各处。

3.1 新增头文件

文件:

src/include/replication/logicalsysrel.h

内容建议:

#ifndef LOGICALSYSREL_H
#define LOGICALSYSREL_H

#include "postgres.h"
#include "utils/rel.h"

extern bool IsLogicalRepSystemRelationOid(Oid relid);
extern bool IsLogicalRepSystemRelation(Relation rel);

#endif

3.2 新增实现文件

文件:

src/backend/replication/logical/logicalsysrel.c

第一版实现:

#include "postgres.h"

#include "catalog/pg_class_d.h"
#include "replication/logicalsysrel.h"
#include "utils/rel.h"

bool
IsLogicalRepSystemRelationOid(Oid relid)
{
    /*
     * 第一版:只允许指定白名单系统表。
     * 这里先写死,后续可替换成 GUC / catalog 配置。
     */
    switch (relid)
    {
        /* 示例:替换为你的目标系统表 OID */
        /* case MySystemCatalogRelationId: */
        /*     return true; */

        default:
            return false;
    }
}

bool
IsLogicalRepSystemRelation(Relation rel)
{
    return rel != NULL &&
           IsCatalogRelation(rel) &&
           IsLogicalRepSystemRelationOid(RelationGetRelid(rel));
}

还要把它加进对应 Makefile

文件:

src/backend/replication/logical/Makefile

加入:

OBJS += logicalsysrel.o

四、补丁一:放开 WAL/logical logging 对白名单系统表的限制

PG18 里 RelationIsLogicallyLogged(relation) 明确排除了 system table;而 RelationIsAccessibleInLogicalDecoding(relation) 只是为了让 decoding snapshot 能访问 catalog 历史状态,并不等于会把它作为复制数据发布出去。(GitHub)

4.1 修改文件

src/include/utils/rel.h

4.2 现状

当前宏等价于:

#define RelationIsLogicallyLogged(relation) \
    (XLogLogicalInfoActive() && \
     RelationNeedsWAL(relation) && \
     (relation)->rd_rel->relkind != RELKIND_FOREIGN_TABLE && \
     !IsCatalogRelation(relation))

这正是系统表在底层被排除的关键之一。(GitHub)

4.3 修改建议

先包含新头文件:

#include "replication/logicalsysrel.h"

然后改成:

#define RelationIsLogicallyLogged(relation) \
    (XLogLogicalInfoActive() && \
     RelationNeedsWAL(relation) && \
     (relation)->rd_rel->relkind != RELKIND_FOREIGN_TABLE && \
     (!IsCatalogRelation(relation) || IsLogicalRepSystemRelation(relation)))

4.4 作用

这样只有白名单系统表才会进入“需要记录足够逻辑信息以供 WAL 提取 tuple change”的路径,其他系统表保持 PG18 原行为不变。(GitHub)


五、补丁二:publication 层允许白名单系统表加入

PG18 里 check_publication_add_relation() 会直接拒绝 system table,而 is_publishable_class() 又同时用 IsCatalogRelationOid(relid)relid >= FirstNormalObjectId 两个条件排除系统表。后者很重要:很多系统表即使你绕过 catalog 检查,也仍会因为 OID 太小被拦下。(Doxygen PostgreSQL)

5.1 修改文件

src/backend/catalog/pg_publication.c

5.2 增加 include

#include "replication/logicalsysrel.h"

5.3 修改 check_publication_add_relation()

原逻辑片段:

/* Can't be system table */
if (IsCatalogRelation(targetrel))
    ereport(ERROR,
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
             errmsg(errormsg, RelationGetRelationName(targetrel)),
             errdetail("This operation is not supported for system tables.")));

改成:

/* Can't be system table unless explicitly allowed */
if (IsCatalogRelation(targetrel) &&
    !IsLogicalRepSystemRelation(targetrel))
    ereport(ERROR,
            (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
             errmsg(errormsg, RelationGetRelationName(targetrel)),
             errdetail("This operation is not supported for system tables.")));

5.4 修改 is_publishable_class()

原逻辑:

static bool
is_publishable_class(Oid relid, Form_pg_class reltuple)
{
    return (reltuple->relkind == RELKIND_RELATION ||
            reltuple->relkind == RELKIND_PARTITIONED_TABLE ||
            reltuple->relkind == RELKIND_SEQUENCE) &&
           !IsCatalogRelationOid(relid) &&
           reltuple->relpersistence == RELPERSISTENCE_PERMANENT &&
           relid >= FirstNormalObjectId;
}

改成:

static bool
is_publishable_class(Oid relid, Form_pg_class reltuple)
{
    bool is_whitelisted_sysrel = IsLogicalRepSystemRelationOid(relid);

    return (reltuple->relkind == RELKIND_RELATION ||
            reltuple->relkind == RELKIND_PARTITIONED_TABLE ||
            reltuple->relkind == RELKIND_SEQUENCE) &&
           (!IsCatalogRelationOid(relid) || is_whitelisted_sysrel) &&
           reltuple->relpersistence == RELPERSISTENCE_PERMANENT &&
           (relid >= FirstNormalObjectId || is_whitelisted_sysrel);
}

5.5 为什么两处都要改

因为 check_publication_add_relation() 负责 SQL/API 入口,而 is_publishable_class() / is_publishable_relation() 负责 publication 资格判断的底层统一逻辑。只改其中一处都不够。PG18 源码注释里还专门提到了 FirstNormalObjectId 这层限制及其历史问题。(Doxygen PostgreSQL)


六、补丁三:禁止通过 schema publication 引入系统表

PG18 里 check_publication_add_schema() 会拒绝 system schema / toast schema。第一版建议保留,不要放开 pg_catalog schema 级发布。(Doxygen PostgreSQL)

6.1 处理建议

不改

check_publication_add_schema()

这样第一版只允许:

ALTER PUBLICATION pub ADD TABLE pg_catalog.xxx;

不允许:

CREATE PUBLICATION pub FOR TABLES IN SCHEMA pg_catalog;

6.2 原因

这是为了防止把整套 catalog 无意间纳入 publication,导致 apply 端失控。PG18 当前对 system schema 的拒绝是明确存在的。(Doxygen PostgreSQL)


七、补丁四:pgoutput 层放行,但限制高级特性

pgoutput_change() 和 TRUNCATE 路径都直接调用 is_publishable_relation(relation);只要上一步已经放开白名单系统表,这里大概率不需要改主判断。(Doxygen PostgreSQL)

7.1 修改文件

src/backend/replication/pgoutput/pgoutput.c

7.2 增加 include

#include "replication/logicalsysrel.h"

7.3 不改 pgoutput_change() 主入口

保留:

if (!is_publishable_relation(relation))
    return;

因为 publication 层已经接管了“谁可发布”的判断。pgoutput_change() 当前正是这么依赖 is_publishable_relation() 的。(Doxygen PostgreSQL)

7.4 修改 get_rel_sync_entry(),禁用 row filter / column list

第一版建议给系统表加保护:

伪代码如下:

static RelationSyncEntry *
get_rel_sync_entry(PGOutputData *data, Relation relation)
{
    RelationSyncEntry *entry;
    Oid relid = RelationGetRelid(relation);

    entry = ... existing code ...;

    if (IsLogicalRepSystemRelationOid(relid))
    {
        /*
         * 第一版不支持对白名单系统表使用:
         * - row filter
         * - column list
         * - generated columns 特殊发布控制
         *
         * 如果 publication 元数据中配置了这些能力,直接报错。
         */
        if (entry->publish_as_relid != relid)
            ereport(ERROR,
                    (errmsg("system relation \"%s\" does not support publish_via_partition_root",
                            RelationGetRelationName(relation))));

        if (entry->exprstate != NULL)
            ereport(ERROR,
                    (errmsg("system relation \"%s\" does not support row filters",
                            RelationGetRelationName(relation))));

        if (entry->columns != NULL)
            ereport(ERROR,
                    (errmsg("system relation \"%s\" does not support column lists",
                            RelationGetRelationName(relation))));
    }

    return entry;
}

7.5 为什么要这么做

因为 get_rel_sync_entry() 是 pgoutput 为 relation 构建发布描述、过滤器、列映射缓存的集中位置;而系统表第一版只应该走最简单的“全列、无过滤”路径,避免把 catalog 复制复杂度带到 publication 元数据层。(Doxygen PostgreSQL)


八、补丁五:订阅端允许白名单系统表作为 apply 目标

这是最关键的另一半。PG18 的 CheckSubscriptionRelkind() 只接受普通表、分区表和 sequence;官方文档也写明 subscriber 端目标只支持 regular table。(Doxygen PostgreSQL)

8.1 修改文件一

src/backend/executor/execReplication.c

8.2 增加 include

#include "replication/logicalsysrel.h"

8.3 保守修改 CheckSubscriptionRelkind()

这个函数当前只拿到了 localrelkind / remoterelkind / 名字,没有本地 relid,因此不适合在这里直接用 OID 白名单判断。更稳的改法是:

  • 尽量少改这个函数
  • 把“是否允许系统表”前移到 logicalrep_rel_open()
  • CheckSubscriptionRelkind() 保持现有 relkind 兼容语义

也就是这一步建议 **不直接大改 CheckSubscriptionRelkind()**,只在必要时保留原表/分区表/sequence 检查。这样能减少对全局逻辑的影响。当前函数的限制条件和 sequence 精确匹配逻辑就在这里。(Doxygen PostgreSQL)


8.4 修改文件二

src/backend/replication/logical/relation.c

8.5 增加 include

#include "replication/logicalsysrel.h"

8.6 在 logicalrep_rel_open() 中前移系统表白名单检查

思路是:

  1. 远端 relation message 收到后,按本地同名 relation 打开

  2. 打开后,如果本地是 system table:

    • 只有白名单表允许继续
    • 非白名单仍报错
  3. 然后再执行原有的 CheckSubscriptionRelkind()

伪代码:

LogicalRepRelMapEntry *
logicalrep_rel_open(LogicalRepRelId remoteid, LOCKMODE lockmode)
{
    LogicalRepRelMapEntry *entry;
    Relation    localrel;
    Oid         relid;

    entry = ... existing lookup ...;

    if (!OidIsValid(entry->localreloid))
        return entry;

    relid = entry->localreloid;
    localrel = table_open(relid, lockmode);

    /*
     * 新增:对本地系统表做白名单控制
     */
    if (IsCatalogRelation(localrel) &&
        !IsLogicalRepSystemRelation(localrel))
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("cannot use relation \"%s.%s\" as logical replication target",
                        entry->remoterel.nspname,
                        entry->remoterel.relname),
                 errdetail("This operation is not supported for system tables.")));

    /*
     * 保持原有 relkind 检查
     */
    CheckSubscriptionRelkind(localrel->rd_rel->relkind,
                             entry->remoterel.relkind,
                             entry->remoterel.nspname,
                             entry->remoterel.relname);

    ...
}

8.7 为什么放在这里更合适

因为这里已经拿到了本地实际 relation,可以同时判断:

  • 是否 system table
  • 是否白名单
  • relkind 是否兼容

CheckSubscriptionRelkind() 只拿到了字符型 relkind 和名字,缺少 OID / Relation 上下文,不适合做系统表白名单控制。logicalrep_rel_open() 本来就是 subscriber 端 relation mapping 的主入口。(Doxygen PostgreSQL)


九、补丁六:限制 initial copy

由于官方文档已经明确 subscriber 目标只支持 regular table,第一版不建议动 tablesync / copy path。(GitHub)

9.1 实现建议

第一版直接在创建订阅或 relation sync 阶段拒绝:

  • 如果 publication 里有白名单系统表
  • copy_data = true
  • 则报错

建议在 subscription 端 relation sync 初始化附近加一层检查,伪代码:

if (is_sysrel_replication_enabled_for_subscription(subid) &&
    copy_data)
    ereport(ERROR,
            (errmsg("initial copy is not supported for logical replication of system relations")));

9.2 原因

system table 的初始一致性问题,比增量 DML 重放更难。第一版只做 streaming apply,成本最低。


十、补丁七:限制 UPDATE/DELETE 的复制身份

这一点你在实现时一定要加。

虽然 subscriber 端 apply 框架能走普通表写入路径,但系统表如果没有合适的 replica identity / 唯一键,UPDATE/DELETE 基本无法安全定位目标行。subscriber 端现有逻辑复制本来就是围绕 table/sequence 的对象类型兼容和关系映射设计的。(Doxygen PostgreSQL)

10.1 建议做法

在 publisher 端检查白名单系统表时增加限制:

  • 必须有主键,或者
  • 必须 REPLICA IDENTITY FULL

建议在 publication add relation 阶段附加检查:

static void
check_logical_sysrel_replica_identity(Relation rel)
{
    if (rel->rd_rel->relreplident != REPLICA_IDENTITY_FULL &&
        RelationGetReplicaIndex(rel) == InvalidOid)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
                 errmsg("cannot add system relation \"%s\" to publication",
                        RelationGetRelationName(rel)),
                 errdetail("Logical replication of system relations requires a replica identity.")));
}

然后在 check_publication_add_relation() 里对系统表调用它。


十一、建议增加一个总开关 GUC

第一版如果只写死白名单,编译期改表 OID 很麻烦。更实用的是加一个 GUC。

11.1 参数建议

logical_replication_system_relations = 'pg_catalog.my_sys_tbl,pg_catalog.my_other_tbl'

11.2 实现方式

  • 定义 char *logical_replication_system_relations
  • postmaster/reload 级参数均可,第一版建议 PGC_SUSET
  • 启动时或首次使用时解析为 OID 缓存列表

11.3 优点

这样无需改代码就能切换目标系统表,适合你做实验和灰度。


十二、近似补丁汇总

下面给你一个“能指导编码”的最小补丁清单。

12.1 新增文件

  • src/include/replication/logicalsysrel.h
  • src/backend/replication/logical/logicalsysrel.c

12.2 修改文件

  • src/include/utils/rel.h
  • src/backend/catalog/pg_publication.c
  • src/backend/replication/pgoutput/pgoutput.c
  • src/backend/replication/logical/relation.c
  • src/backend/replication/logical/Makefile

12.3 关键改动点

rel.h

+#include "replication/logicalsysrel.h"

 #define RelationIsLogicallyLogged(relation) \
     (XLogLogicalInfoActive() && \
      RelationNeedsWAL(relation) && \
      (relation)->rd_rel->relkind != RELKIND_FOREIGN_TABLE && \
-     !IsCatalogRelation(relation))
+     (!IsCatalogRelation(relation) || IsLogicalRepSystemRelation(relation)))

pg_publication.c

+#include "replication/logicalsysrel.h"

- if (IsCatalogRelation(targetrel))
+ if (IsCatalogRelation(targetrel) &&
+     !IsLogicalRepSystemRelation(targetrel))
     ereport(ERROR, ...);

- !IsCatalogRelationOid(relid) &&
+ (!IsCatalogRelationOid(relid) || IsLogicalRepSystemRelationOid(relid)) &&

- relid >= FirstNormalObjectId;
+ (relid >= FirstNormalObjectId || IsLogicalRepSystemRelationOid(relid));

pgoutput.c

+#include "replication/logicalsysrel.h"

并在 get_rel_sync_entry() 里对系统表禁用 row filter / column list。

relation.c

+#include "replication/logicalsysrel.h"

并在 logicalrep_rel_open() 中新增:

if (IsCatalogRelation(localrel) &&
    !IsLogicalRepSystemRelation(localrel))
    ereport(ERROR, ...);

十三、编译后你该怎么验证

建议用一张“你自己可控的系统样式表”先做,不要一上来就碰核心 catalog。

13.1 验证顺序

先验证这几步:

  1. publication 能否成功加入目标系统表
  2. 目标系统表 INSERT 是否能被 decode 并发出
  3. subscriber 是否能打开本地同名表
  4. INSERT 是否能 apply 成功
  5. 再测 UPDATE / DELETE
  6. 最后测异常回放、重启恢复、slot 重连

13.2 最小测试 SQL

发布端:

ALTER PUBLICATION pub ADD TABLE pg_catalog.my_sys_tbl;

订阅端:

CREATE SUBSCRIPTION sub
CONNECTION '...'
PUBLICATION pub
WITH (copy_data = false);

然后在发布端对该系统表做:

INSERT INTO pg_catalog.my_sys_tbl ...;
UPDATE pg_catalog.my_sys_tbl ...;
DELETE FROM pg_catalog.my_sys_tbl ...;

十四、最重要的风险提醒

这个补丁“能跑”不代表“对所有系统表都安全”。

原因很简单:PG18 当前把 system table 排除在 publishable / subscriber target 之外,不只是保守,而是因为很多 catalog 变更并不等价于普通 heap DML。系统表内容默认不做逻辑抽取所需记录,publication 入口直接拒绝,subscriber 目标类型也被限制,官方文档也明确只支持 regular table 作为目标,这些都说明“catalog 复制”天然超出原生逻辑复制的默认语义边界。(GitHub)

所以第一版最稳的策略仍然是:

  • 只放开一张或极少数白名单系统表
  • 最好是你为了 DDL/元数据同步而专门设计的 catalog-like 表
  • 不要直接碰 pg_classpg_attributepg_dependpg_authid 这类核心 catalog

如果你愿意,我下一条可以继续直接给你一版 统一 diff 风格的 patch 草稿,按文件分段写成更像 git diff 的形式。


  目录
分类导航
随笔2 AI27 算法1 计算机基础13 博客搭建7 ChatGPT2 集群63 计算机通信1 数据库34 数据库深入80 DPDK26 Docker11 Elasticsearch4 编辑工具4 FAQ1 Go Web1 hometown2 编程语言16 网络9 OPC1 Linux38 openGauss4 页面12 PostgreSQL54 程序员自我修养1 协议11 成长之路1 stock1 存储5 工具20 VPP18 视频作品1 Vue13 Web1 代码示例11 数据库15 BenchmarkSQL1 PostgreSQL 源码修炼之路14
最热文章
1
13 逻辑复制深入
数据库深入🔥 1570
2
0 Postgresql存储、索引及系统优化、主备切换
PostgreSQL🔥 1495
3
一文读懂openguass dcf网络模块
集群🔥 1420
4
逻辑复制源码分析
数据库深入🔥 1327
5
PostgreSQL 分区表:从一行 `PARTITION BY` 到路由热路径的全链路拆解
数据库🔥 1094
6
applyparallelworker.c 之 LA 端源码深度解析:Leader Apply Worker 的指挥中枢
数据库深入🔥 1082
7
PostgreSQL Background Worker 全解:从 `RegisterBackgroundWorker` 到逻辑复制 4 类 worker 的全生命周期
数据库🔥 1078
8
PostgreSQL的后台进程walsender分析 - 关系型数据库 - 亿速云
PostgreSQL🔥 1033
9
PostgreSQL 逻辑复制的监控:六张视图 + 一组可执行 SQL,把 publisher/subscriber 的速率与健康度彻底看透
数据库🔥 1032
10
PostgreSQL 逻辑复制支持 DDL 之后:DDL 与 DML 的时序难题(重点:分区表)
数据库🔥 999
11
reorderbuffer.c 源码深度解析:PostgreSQL 逻辑复制的"事务重组引擎
数据库深入🔥 953
12
PostgreSQL 内核开发:读取一张表的 9 步标准流程与缓存全景
数据库🔥 938
13
从 `postgres` 二进制到生产级守护 —— PostgreSQL 最外层模块与启动全流程拆解
数据库🔥 936
14
支持逻辑复制同步 DDL 适配 SQL Server 方案
数据库深入🔥 934
15
PostgreSQL 逻辑复制的 ReorderBuffer 与事务机制:从一行 WAL 到一致性变更流的全链路绑定
数据库🔥 913
16
DDL同步架构(美化版)
数据库深入🔥 908
17
PostgreSQL Latch 机制详解:从一行 SetLatch 到 epoll 的内核之旅
数据库🔥 871
18
pgbench 源码全解:一个 C 文件如何撑起 PostgreSQL 官方压测工具
数据库🔥 860
19
PostgreSQL libpq 机制与缓冲区详解
数据库🔥 850
20
PostgreSQL 逻辑复制 spill 文件深度剖析:从 `xid-*.spill` 到 TPC-C 的增长方程
数据库🔥 845