
# RDS for PostgreSQL发布与订阅
#### 逻辑定义
发布可以被定义在任何物理复制的主服务器上。定义有发布的节点被称为发布者。发布是从一个表或者一组表生成的改变的集合，也可以被描述为更改集合或者复制集合。每个发布都只存在于一个数据库中。
订阅是逻辑复制的下游端。订阅被定义在其中的节点被称为订阅者。一个订阅会定义到另一个数据库的连接以及它想要订阅的发布集合（一个或者多个）。逻辑订阅者的行为与一个普通的PostgreSQL实例（主库）无异，逻辑订阅者也可以创建自己的发布，拥有自己的订阅者。
#### 使用权限
- 创建发布时，发布者必须有流复制权限。即replication权限。
- 创建发布时，使用all tables发布所有表时，需确保发布者是**提权起始及之后版本**的root用户。
- 创建/删除订阅时，需确保订阅者是**提权起始及之后版本**的root用户。
- 创建发布/订阅时，需确保发布端和订阅端实例在同一VPC下。
各个版本root用户提权情况参见[root用户权限说明](https://support.huaweicloud.com/rds-pg_faq/rds_faq_0300.html#section2)。
#### 发布使用限制
- 发布目前只能包含表（即：索引，序列号，物化视图这些不会被发布），每个表可以添加到多个发布中。
- 一个publication允许有多个订阅者。
- 允许使用all tables发布所有表。
- 在同一个数据库中，可以创建多个publication，但是不能重名。已创建的publication可以通过查询pg_publication获取。
- 发布可以筛选所需的变更类型：包括insert、update、delete 和truncate的任意组合，类似触发器事件，默认所有变更都会被发布。 例如：发布表t1的**update** 和**delete**操作。
  ```
  CREATE PUBLICATION update_delete_only FOR TABLE t1
      WITH (publish = 'update, delete') ;
  ```
  
- 复制标识：当发布了表的**update** , **delete** 时，表必须设置复制标识（Replica Identity），如果设置了nothing，则执行**update** , **delete** 时会报错。
  表上的复制标识可以通过查阅pg_class.relreplident获取。
  这是一个字符类型的"枚举"，标识用于组装 "复制标识" 的列：d = default ，f = 所有的列，i 使用特定的索引，n 没有复制标识。
  表上是否具有可用作复制标识的索引约束，可以通过以下查询获取：
  ```
  SELECT quote_ident(nspname) || '.' || quote_ident(relname) AS name, con.ri AS keys,
         CASE relreplident WHEN 'd' THEN 'default' WHEN 'n' THEN 'nothing' WHEN 'f' THEN 'full' WHEN 'i' THEN 'index' END AS replica_identity
  FROM pg_class c JOIN pg_namespace n ON c.relnamespace = n.oid, LATERAL (SELECT array_agg(contype) AS ri FROM pg_constraint WHERE conrelid = c.oid) con
  WHERE relkind = 'r' AND nspname NOT IN ('pg_catalog', 'information_schema', 'monitor', 'repack', 'pg_toast')
  ORDER BY 2,3;
  ```
  
- 复制标识配置 表到复制标识可以通过**ALTER TABLE**进行修改。
  ```
  ALTER TABLE table_name REPLICA IDENTITY 
  { DEFAULT | USING INDEX index_name | FULL | NOTHING };
  -- 具体有四种形式
  ALTER TABLE t_normal REPLICA IDENTITY DEFAULT;                    -- 使用主键，如果没有主键则为FULL
  ALTER TABLE t_normal REPLICA IDENTITY FULL;                       -- 使用整行作为标识
  ALTER TABLE t_normal REPLICA IDENTITY USING INDEX t_normal_v_key; -- 使用唯一索引
  ALTER TABLE t_normal REPLICA IDENTITY NOTHING;                    -- 不设置复制标识
  ```
  
- 复制标识在实际使用中的注意事项
  - 表上有主键，使用默认的default复制标识。
  
  - 表上没有主键，但是有非空唯一索引，显式配置index复制标识。
  
  - 表上既没有主键，也没有非空唯一索引，显式配置full复制标识（运行效率非常低，仅能作为兜底方案）。
  
  - 其他所有情况，都无法正常完成逻辑复制功能。输出的信息不足，可能会报错。
  
  - 特别需要注意：如果nothing复制标识的表纳入到逻辑复制中，对其进行删改会导致发布端报错。
   
 
#### 订阅使用限制
- 为了确保使用Failover Slot，必须在发布端手工创建逻辑复制槽(Failover Slot)，并通过**create_slot = false** 关联已有复制槽，如下：
  ```
  CREATE SUBSCRIPTION sub1 CONNECTION 'host=192.168.0.1 port=5432 user=user1 dbname=db1' PUBLICATION pub_name with (create_slot = false,slot_name = FailoverSlot_name);
  ```
  
- 逻辑复制不会复制DDL变更，因此发布集中的表必须已经存在于订阅端上。
- 同一个数据库中，可以创建多个subscription，这些subscription可以来自一个或多个发布者。
- 订阅者的同一张表，不能接受来自同一个源的多个发布。
- 在创建subscription或者alter subscription时，可以使用enable来启用该订阅，或者使用disable暂停该订阅。
- 如果要完全删除订阅，使用**DROP SUBSCRIPTION** ，注意，删除订阅后，本地的表不会被删除，数据也不会清除，仅仅是不再接收该订阅的上游信息。
  ![](https://support.huaweicloud.com/bestpractice-rds-pg/public_sys-resources/notice_3.0-zh-cn.png)
  如果订阅与复制槽相关联，就不能在事务块内部执行**DROP SUBSCRIPTION** 。可以使用**ALTER SUBSCRIPTION**取消关联复制槽。
  删除订阅可参考以下步骤：
  1. 在订阅端查询订阅关联的复制槽。
     ```
     select subname,subconninfo,subslotname from pg_subscription where subname = 'sub2';
     ```
     - subname为订阅者名称。
     
     - subconninfo为连接远程主机信息。
     
     - subslotname 为远程主机复制槽名称。
      
  
  2. 在订阅端执行**ALTER SUBSCRIPTION** 取消关联复制槽并删除。
     ```
     ALTER SUBSCRIPTION subname SET (slot_name = NONE);
     DROP SUBSCRIPTION subname;
     ```
     
  
  3. 在发布端删除关联的复制槽。
     ```
     select pg_drop_replication_slot('slot_name');
     ```
     
   
 
#### 语法参考
- 发布 **CREATE PUBLICATION** 用于创建发布，**DROP PUBLICATION** 用于移除发布，**ALTER PUBLICATION**用于修改发布。
  发布创建之后，可以通过**ALTER PUBLICATION**动态地向发布中添加或移除表，这些操作都是事务性的。
  

- 订阅 **CREATE SUBSCRIPTION** 用于创建订阅，**DROP SUBSCRIPTION** 用于移除订阅，**ALTER SUBSCRIPTION**用于修改订阅。
  订阅创建之后，可以通过**ALTER SUBSCRIPTION** 随时**暂停** 与**恢复** 订阅。移除并重建订阅会导致**同步信息丢失**，这意味着相关数据需要重新进行同步。
  
具体使用说明请参考以下官方文档，以PostgreSQL 13版本为例：
- 创建发布：<https://www.postgresql.org/docs/13/sql-createpublication.html>
- 删除发布：<https://www.postgresql.org/docs/13/sql-droppublication.html>
- 修改发布：<https://www.postgresql.org/docs/13/sql-alterpublication.html>
 
