

# 使用 pglogical 跨实例同步数据
<a name="Appendix.PostgreSQL.CommonDBATasks.pglogical"></a>

所有当前可用的 RDS for PostgreSQL 版本都支持 `pglogical` 扩展。pglogic 扩展早于 PostgreSQL 在版本 10 中引入的功能类似的逻辑复制特征。有关更多信息，请参阅[为 Amazon RDS for PostgreSQL 执行逻辑复制](PostgreSQL.Concepts.General.FeatureSupport.LogicalReplication.md)。

`pglogical` 扩展支持在两个或更多 RDS for PostgreSQL 数据库实例之间进行逻辑复制。它还支持在不同的 PostgreSQL 版本之间进行复制，以及在 RDS for PostgreSQL 数据库实例和 Aurora PostgreSQL 数据库集群上运行的数据库之间进行复制。`pglogical` 扩展使用发布-订阅模型将对表和其他对象（例如序列）的更改从发布者复制到订阅者。它依赖于复制插槽来确保更改从发布者节点同步到订阅者节点，定义如下。
+ *发布者节点*是作为要复制到其他节点的数据来源的 RDS for PostgreSQL 数据库实例。发布者节点定义要在发布集中复制的表。
+ *订阅者节点*是用于接收来自发布商的 WAL 更新的 RDS for PostgreSQL 数据库实例。订阅者创建订阅以连接到发布者并获取解码后的 WAL 数据。订阅者创建订阅时，将在发布者节点上创建复制插槽。

在下文中，您可以了解有关设置 `pglogical` 扩展的信息。

**Topics**
+ [pglogical 扩展的要求和限制](#Appendix.PostgreSQL.CommonDBATasks.pglogical.requirements-limitations)
+ [设置 pglogical 扩展](Appendix.PostgreSQL.CommonDBATasks.pglogical.basic-setup.md)
+ [为 RDS for PostgreSQL 数据库实例设置逻辑复制](Appendix.PostgreSQL.CommonDBATasks.pglogical.setup-replication.md)
+ [在主要升级后重新建立逻辑复制](Appendix.PostgreSQL.CommonDBATasks.pglogical.recover-replication-after-upgrade.md)
+ [管理 RDS for PostgreSQL 的逻辑复制插槽](Appendix.PostgreSQL.CommonDBATasks.pglogical.handle-slots.md)
+ [pglogical 扩展的参数参考](Appendix.PostgreSQL.CommonDBATasks.pglogical.reference.md)

## pglogical 扩展的要求和限制
<a name="Appendix.PostgreSQL.CommonDBATasks.pglogical.requirements-limitations"></a>

所有当前可用的 RDS for PostgreSQL 版本都支持 `pglogical` 扩展。

发布者节点和订阅者节点都必须设置为进行逻辑复制。

要从发布者复制到订阅用户的表必须具有相同的名称和相同的架构。这些表还必须包含相同的列，并且这些列必须使用相同的数据类型。发布者表和订阅者表必须具有相同的主键。我们建议您仅使用 PRIMARY KEY 作为唯一约束。

对于 CHECK 约束和 NOT NULL 约束，订阅者节点上的表可能比发布者节点上的表具有更宽松的约束。

`pglogical` 扩展提供了诸如双向复制之类的特征，PostgreSQL（版本 10 及更高版本）中内置的逻辑复制特征不支持这些特征。有关更多信息，请参阅[使用 pglogic 进行 PostgreSQL 双向复制](https://aws.amazon.com/blogs/database/postgresql-bi-directional-replication-using-pglogical/)。

# 设置 pglogical 扩展
<a name="Appendix.PostgreSQL.CommonDBATasks.pglogical.basic-setup"></a>

要在 RDS for PostgreSQL 数据库实例上设置 `pglogical` 扩展，首先要将 `pglogical` 添加到 RDS for PostgreSQL 数据库实例的自定义数据库参数组上的共享库中。您还需要将 `rds.logical_replication` 参数的值设置为 `1`，以开启逻辑解码。最后，在数据库中创建此扩展。您可以使用 AWS 管理控制台或 AWS CLI 执行这些任务。

您必须拥有 `rds_superuser` 角色的权限才能执行这些任务。

以下步骤假设您的 RDS for PostgreSQL 数据库实例与自定义数据库参数组相关联。有关创建自定义数据库参数组的信息，请参阅 [Amazon RDS 的参数组](USER_WorkingWithParamGroups.md)。

## 控制台
<a name="Appendix.PostgreSQL.CommonDBATasks.pglogical.basic-setup.CON"></a>

**设置 pglogical 扩展**

1. 登录 AWS 管理控制台 并通过以下网址打开 Amazon RDS 控制台：[https://console.aws.amazon.com/rds/](https://console.aws.amazon.com/rds/)。

1. 在导航窗格中，选择 RDS for PostgreSQL 数据库实例。

1. 打开 的**配置**选项卡。RDS for PostgreSQL 数据库实例的 Configuration（配置）选项卡。在实例详细信息中，找到 **Parameter group**（参数组）链接。

1. 选择此链接以打开与您的 RDS for PostgreSQL 数据库实例关联的自定义参数。

1. 在 **Parameters**（参数）搜索字段中，键入 `shared_pre` 以查找 `shared_preload_libraries` 参数。

1. 选择 **Edit parameters**（编辑参数）以访问属性值。

1. 将 `pglogical` 添加到 **Values**（值）字段的列表中。使用逗号分隔值列表中的项目。  
![\[添加了 pglogical 的 shared_preload_libraries 参数的图像。\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/images/apg_rpg_shared_preload_pglogical.png)

1. 找到 `rds.logical_replication` 参数并将其设置为 `1`，以开启逻辑复制。

1. 重启 RDS for PostgreSQL 数据库实例，以使更改生效。

1. 当实例可用时，可以使用 `psql`（或 pgAdmin）连接到 RDS for PostgreSQL 数据库实例。

   ```
   psql --host=111122223333.aws-region.rds.amazonaws.com --port=5432 --username=postgres --password --dbname=labdb
   ```

1. 要验证 pglogical 是否初始化，可以运行以下命令。

   ```
   SHOW shared_preload_libraries;
   shared_preload_libraries 
   --------------------------
   rdsutils,pglogical
   (1 row)
   ```

1. 验证启用逻辑解码的设置，如下所示。

   ```
   SHOW wal_level;
   wal_level
   -----------
    logical
   (1 row)
   ```

1. 创建扩展，如下所示。

   ```
   CREATE EXTENSION pglogical;
   EXTENSION CREATED
   ```

1. 选择**保存更改**。

1. 通过以下网址打开 Amazon RDS 控制台：[https://console.aws.amazon.com/rds/](https://console.aws.amazon.com/rds/)。

1. 从数据库列表中选择 RDS for PostgreSQL 数据库实例以将其选中，然后从 Actions（操作）菜单中选择 **Reboot**（重启）。

## AWS CLI
<a name="Appendix.PostgreSQL.CommonDBATasks.pglogical.basic-setup.CLI"></a>

**设置 pglogical 扩展**

要使用 AWS CLI 设置 pglogical，您可以调用 [modify-db-parameter-group](https://docs.aws.amazon.com/cli/latest/reference/rds/modify-db-parameter-group.html) 操作来修改自定义参数组中的某些参数，如以下过程所示。

1. 使用以下 AWS CLI 命令向 `shared_preload_libraries` 参数中添加 `pglogical`。

   ```
   aws rds modify-db-parameter-group \
      --db-parameter-group-name custom-param-group-name \
      --parameters "ParameterName=shared_preload_libraries,ParameterValue=pglogical,ApplyMethod=pending-reboot" \
      --region aws-region
   ```

1. 使用以下 AWS CLI 命令将 `rds.logical_replication` 设置为 `1`，以针对 RDS for PostgreSQL 数据库实例开启逻辑解码功能。

   ```
   aws rds modify-db-parameter-group \
      --db-parameter-group-name custom-param-group-name \
      --parameters "ParameterName=rds.logical_replication,ParameterValue=1,ApplyMethod=pending-reboot" \
      --region aws-region
   ```

1. 使用以下 AWS CLI 命令重启 RDS for PostgreSQL 数据库实例，以便初始化 pglogical 库。

   ```
   aws rds reboot-db-instance \
       --db-instance-identifier your-instance \
       --region aws-region
   ```

1. 当实例可用时，使用 `psql` 连接到 RDS for PostgreSQL 数据库实例。

   ```
   psql --host=111122223333.aws-region.rds.amazonaws.com --port=5432 --username=postgres --password --dbname=labdb
   ```

1. 创建扩展，如下所示。

   ```
   CREATE EXTENSION pglogical;
   EXTENSION CREATED
   ```

1. 使用以下 AWS CLI 命令重启 RDS for PostgreSQL 数据库实例。

   ```
   aws rds reboot-db-instance \
       --db-instance-identifier your-instance \
       --region aws-region
   ```

# 为 RDS for PostgreSQL 数据库实例设置逻辑复制
<a name="Appendix.PostgreSQL.CommonDBATasks.pglogical.setup-replication"></a>

以下过程说明如何在两个 RDS for PostgreSQL 数据库实例之间启动逻辑复制。这些步骤假设来源（发布者）和目标（订阅者）都如[设置 pglogical 扩展](Appendix.PostgreSQL.CommonDBATasks.pglogical.basic-setup.md)中所述设置了 `pglogical` 扩展。

**注意**  
订阅用户节点的 `node_name` 不能以 `rds` 开头。

**创建发布者节点并定义要复制的表**

这些步骤假设您的 RDS for PostgreSQL 数据库实例有一个数据库，其中包含一个或多个您要复制到另一个节点的表。您需要在订阅者上根据发布者重新创建表结构，因此，如果需要，首先获取表结构。为此，您可以使用 `psql` 元命令 `\d tablename`，然后在订阅者实例上创建相同的表。以下过程在发布者（来源）上创建示例表以用于演示目的。

1. 使用 `psql` 连接到具有要用作订阅者来源的表的实例。

   ```
   psql --host=source-instance.aws-region.rds.amazonaws.com --port=5432 --username=postgres --password --dbname=labdb
   ```

   如果没有现有表要复制，可以按如下方式创建示例表。

   1. 使用以下 SQL 语句创建一个示例表。

      ```
      CREATE TABLE docs_lab_table (a int PRIMARY KEY);
      ```

   1. 使用以下 SQL 语句用生成的数据填充表。

      ```
      INSERT INTO docs_lab_table VALUES (generate_series(1,5000));
      INSERT 0 5000
      ```

   1. 使用以下 SQL 语句验证表中是否存在数据。

      ```
      SELECT count(*) FROM docs_lab_table;
      ```

1. 将这一 RDS for PostgreSQL 数据库实例标识为发布者节点，如下所示。

   ```
   SELECT pglogical.create_node(
       node_name := 'docs_lab_provider',
       dsn := 'host=source-instance.aws-region.rds.amazonaws.com port=5432 dbname=labdb');
    create_node
   -------------
      3410995529
   (1 row)
   ```

1. 将要复制的表添加到默认的复制集。有关复制集的更多信息，请参阅 pglogical 文档中的[复制集](https://github.com/2ndQuadrant/pglogical/tree/REL2_x_STABLE/docs#replication-sets)。

   ```
   SELECT pglogical.replication_set_add_table('default', 'docs_lab_table', 'true', NULL, NULL);
    replication_set_add_table
     ---------------------------
     t
     (1 row)
   ```

发布者节点设置已完成。现在，您可以设置订阅者节点以接收来自发布者的更新。

**设置订阅者节点并创建订阅以接收更新**

这些步骤假设已使用 `pglogical` 扩展设置了 RDS for PostgreSQL 数据库实例。有关更多信息，请参阅 [设置 pglogical 扩展](Appendix.PostgreSQL.CommonDBATasks.pglogical.basic-setup.md)。

1. 使用 `psql` 连接到要从发布者接收更新的实例。

   ```
   psql --host=target-instance.aws-region.rds.amazonaws.com --port=5432 --username=postgres --password --dbname=labdb
   ```

1. 在订阅者 RDS for PostgreSQL 数据库实例上，创建与发布者上存在的相同表。在本例中，该表为 `docs_lab_table`。您可以按如下所示创建表。

   ```
   CREATE TABLE docs_lab_table (a int PRIMARY KEY);
   ```

1. 验证此表为空。

   ```
   SELECT count(*) FROM docs_lab_table;
    count
   -------
     0
   (1 row)
   ```

1. 将这一 RDS for PostgreSQL 数据库实例标识为订阅者节点，如下所示。

   ```
   SELECT pglogical.create_node(
       node_name := 'docs_lab_target',
       dsn := 'host=target-instance.aws-region.rds.amazonaws.com port=5432 sslmode=require dbname=labdb user=postgres password=********');
    create_node
   -------------
      2182738256
   (1 row)
   ```

1. 创建订阅。

   ```
   SELECT pglogical.create_subscription(
      subscription_name := 'docs_lab_subscription',
      provider_dsn := 'host=source-instance.aws-region.rds.amazonaws.com port=5432 sslmode=require dbname=labdb user=postgres password=*******',
      replication_sets := ARRAY['default'],
      synchronize_data := true,
      forward_origins := '{}' );  
    create_subscription
   ---------------------
   1038357190
   (1 row)
   ```

   完成此步骤后，将在订阅者上的表中创建发布者上表中的数据。您可以使用以下 SQL 查询来验证是否已发生这种情况。

   ```
   SELECT count(*) FROM docs_lab_table;
    count
   -------
     5000
   (1 row)
   ```

此后，对发布者上的表所做的更改将复制到订阅者上的表中。

# 在主要升级后重新建立逻辑复制
<a name="Appendix.PostgreSQL.CommonDBATasks.pglogical.recover-replication-after-upgrade"></a>

对于设置为逻辑复制的发布者节点的 RDS for PostgreSQL 数据库实例，在可以对其执行主要版本升级之前，您必须删除所有复制插槽，即使是不活动的复制插槽也是如此。我们建议您暂时从发布者节点转移数据库事务，删除复制插槽，升级 RDS for PostgreSQL 数据库实例，然后重新建立并重新启动复制。

复制插槽仅托管在发布者节点上。逻辑复制场景中的 RDS for PostgreSQL 订阅者节点没有可删除的插槽，但当它被指定为对发布者具有订阅的订阅者节点时，它无法升级到主要版本。在升级 RDS for PostgreSQL 订阅者节点之前，请删除订阅和节点。有关更多信息，请参阅。[管理 RDS for PostgreSQL 的逻辑复制插槽](Appendix.PostgreSQL.CommonDBATasks.pglogical.handle-slots.md)

## 确定逻辑复制已中断
<a name="Appendix.PostgreSQL.CommonDBATasks.pglogical.recover-replication-after-upgrade.identifying-the-issue"></a>

您可以通过查询发布者节点或订阅者节点来确定复制过程是否已中断，如下所示。

**检查发布者节点**
+ 使用 `psql` 连接到发布者节点，然后查询 `pg_replication_slots` 函数。注意活动列中的值。通常，这将返回 `t`（true），表明复制处于活动状态。如果查询返回 `f`（false），则表明向订阅者的复制已停止。

  ```
  SELECT slot_name,plugin,slot_type,active FROM pg_replication_slots;
                      slot_name              |      plugin      | slot_type | active
  -------------------------------------------+------------------+-----------+--------
   pgl_labdb_docs_labcb4fa94_docs_lab3de412c | pglogical_output | logical   | f
  (1 row)
  ```

**检查订阅者节点**

在订阅者节点上，您可以通过三种不同的方式检查复制的状态。
+ 浏览订阅者节点上的 PostgreSQL 日志，以查找失败消息。该日志使用包含退出代码 1 的消息来标识故障，如下所示。

  ```
  2022-07-06 16:17:03 UTC::@:[7361]:LOG: background worker "pglogical apply 16404:2880255011" (PID 14610) exited with exit code 1
  2022-07-06 16:19:44 UTC::@:[7361]:LOG: background worker "pglogical apply 16404:2880255011" (PID 21783) exited with exit code 1
  ```
+ 查询 `pg_replication_origin` 函数。使用 `psql` 连接到订阅者节点上的数据库并查询 `pg_replication_origin` 函数，如下所示。

  ```
  SELECT * FROM pg_replication_origin;
   roident | roname
  ---------+--------
  (0 rows)
  ```

  结果集为空表示复制已中断。正常情况下，您将看到如下输出。

  ```
     roident |                       roname
    ---------+----------------------------------------------------
           1 | pgl_labdb_docs_labcb4fa94_docs_lab3de412c
    (1 row)
  ```
+ 查询 `pglogical.show_subscription_status` 函数，如以下示例所示。

  ```
  SELECT subscription_name,status,slot_name FROM pglogical.show_subscription_status();
       subscription_name | status |              slot_name
  ---====----------------+--------+-------------------------------------
   docs_lab_subscription | down   | pgl_labdb_docs_labcb4fa94_docs_lab3de412c
  (1 row)
  ```

  此输出显示复制已中断。它的状态为 `down`。通常，输出将状态显示为 `replicating`。

如果您的逻辑复制过程已中断，则可以按照以下步骤重新建立复制。

**在发布者节点和订阅者节点之间重新建立逻辑复制**

要重新建立复制，请先断开订阅者与发布者节点的连接，然后重新建立订阅，如这些步骤所述。

1. 使用 `psql` 连接到订阅者节点，如下所示。

   ```
   psql --host=222222222222.aws-region.rds.amazonaws.com --port=5432 --username=postgres --password --dbname=labdb
   ```

1. 通过使用 `pglogical.alter_subscription_disable` 函数停用订阅。

   ```
   SELECT pglogical.alter_subscription_disable('docs_lab_subscription',true);
    alter_subscription_disable
   ----------------------------
    t
   (1 row)
   ```

1. 通过查询 `pg_replication_origin` 获取发布者节点的标识符，如下所示。

   ```
   SELECT * FROM pg_replication_origin;
    roident |               roname
   ---------+-------------------------------------
          1 | pgl_labdb_docs_labcb4fa94_docs_lab3de412c
   (1 row)
   ```

1. 将上一步的响应与 `pg_replication_origin_create` 命令一起使用，以分配重新建立时订阅可以使用的标识符。

   ```
   SELECT pg_replication_origin_create('pgl_labdb_docs_labcb4fa94_docs_lab3de412c');
     pg_replication_origin_create
   ------------------------------
                               1
   (1 row)
   ```

1. 通过传递其状态为 `true` 的名称来打开订阅，如下面的示例所示。

   ```
   SELECT pglogical.alter_subscription_enable('docs_lab_subscription',true);
     alter_subscription_enable
   ---------------------------
    t
   (1 row)
   ```

检查节点的状态。其状态应为 `replicating`，如本例所示。

```
SELECT subscription_name,status,slot_name
  FROM pglogical.show_subscription_status();
             subscription_name |   status    |              slot_name
-------------------------------+-------------+-------------------------------------
 docs_lab_subscription         | replicating | pgl_labdb_docs_lab98f517b_docs_lab3de412c
(1 row)
```

检查发布者节点上订阅者的复制插槽的状态。插槽的 `active` 列应返回 `t`（true），表示已重新建立复制。

```
SELECT slot_name,plugin,slot_type,active
  FROM pg_replication_slots;
                    slot_name              |      plugin      | slot_type | active
-------------------------------------------+------------------+-----------+--------
 pgl_labdb_docs_lab98f517b_docs_lab3de412c | pglogical_output | logical   | t
(1 row)
```

# 管理 RDS for PostgreSQL 的逻辑复制插槽
<a name="Appendix.PostgreSQL.CommonDBATasks.pglogical.handle-slots"></a>

对于在逻辑复制场景中充当发布者节点的 RDS for PostgreSQL 数据库实例，在对其执行主要版本升级之前，必须删除该实例上的复制插槽。主要版本升级预检查过程会通知您，在插槽被删除之前，升级无法继续。

要从 RDS for PostgreSQL 数据库实例中删除插槽，请先删除订阅，然后删除插槽。

要识别使用 `pglogical` 扩展创建的复制插槽，请登录到每个数据库并获取节点的名称。当您查询订阅者节点时，您会在输出中得到发布者节点和订阅者节点，如本示例所示。

```
SELECT * FROM pglogical.node;
node_id   |     node_name
------------+-------------------
 2182738256 | docs_lab_target
 3410995529 | docs_lab_provider
(2 rows)
```

您可以通过以下查询获取有关订阅的详细信息。

```
SELECT sub_name,sub_slot_name,sub_target
  FROM pglogical.subscription;
 sub_name |         sub_slot_name          | sub_target
----------+--------------------------------+------------
  docs_lab_subscription     | pgl_labdb_docs_labcb4fa94_docs_lab3de412c | 2182738256
(1 row)
```

现在您可以删除订阅，如下所示。

```
SELECT pglogical.drop_subscription(subscription_name := 'docs_lab_subscription');
 drop_subscription
-------------------
                 1
(1 row)
```

删除订阅后，您可以删除该节点。

```
SELECT pglogical.drop_node(node_name := 'docs-lab-subscriber');
 drop_node
-----------
 t
(1 row)
```

您可以验证该节点是否不再存在，如下所示。

```
SELECT * FROM pglogical.node;
 node_id | node_name
---------+-----------
(0 rows)
```

# pglogical 扩展的参数参考
<a name="Appendix.PostgreSQL.CommonDBATasks.pglogical.reference"></a>

在表中，您可以找到与 `pglogical` 扩展关联的参数。`pglogical.conflict_log_level` 和 `pglogical.conflict_resolution` 等参数用于处理更新冲突。当对订阅来自发布者的更改的相同表进行本地更改时，可能会出现冲突。在不同情况下也可能发生冲突，例如双向复制或当多个订阅者从同一个发布者进行复制时。有关更多信息，请参阅[使用 pglogical 进行 PostgreSQL 双向复制](https://aws.amazon.com/blogs/database/postgresql-bi-directional-replication-using-pglogical/)。


| 参数 | 说明 | 
| --- | --- | 
| pglogical.batch\$1inserts | 在可能时执行批量插入。默认情况下未设置。更改为“1”将打开，更改为“0”将关闭。 | 
| pglogical.conflict\$1log\$1level | 设置用于记录已解决的冲突的日志级别。支持的字符串值为 debug5、debug4、debug3、debug2、debug1、info、notice、warning、error、log、fatal、panic。 | 
| pglogical.conflict\$1resolution | 设置在冲突可以解决时用来解决冲突的方法。支持的字符串值为 error、apply\$1remote、keep\$1local、last\$1update\$1wins、first\$1update\$1wins。 | 
| pglogical.extra\$1connection\$1options | 要添加到所有对等节点连接的连接选项。 | 
| pglogical.synchronous\$1commit | pglogical 专用的同步提交值 | 
| pglogical.use\$1spi | 使用 SPI（服务器编程接口）而非低级 API 来应用更改。设置为“1”将打开，设置为“0”将关闭。有关 SPI 的更多信息，请参阅 PostgreSQL 文档中的[服务器编程接口](https://www.postgresql.org/docs/current/spi.html)。 | 