

# 使用 pgactive 支持主动-主动复制
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive"></a>

`pgactive` 扩展使用主动-主动复制来支持和协调多个 RDS for PostgreSQL 数据库上的写入操作。Amazon RDS for PostgreSQL 支持以下版本的 `pgactive` 扩展。
+ RDS for PostgreSQL 17.0 和所有更高版本
+ RDS for PostgreSQL 16.1 及更高的 16 版本
+ RDS for PostgreSQL 15.4-R2 及更高的 15 版本
+ RDS for PostgreSQL 14.10 及更高的 14 版本
+ RDS for PostgreSQL 13.13 及更高的 13 版本
+ RDS for PostgreSQL 12.17 及更高的 12 版本
+ RDS for PostgreSQL 11.22

**注意**  
当复制配置中的多个数据库上有写入操作时，可能会发生冲突。有关更多信息，请参阅 [处理主动-主动复制中的冲突](Appendix.PostgreSQL.CommonDBATasks.pgactive.handle-conflicts.md)。

**Topics**
+ [pgactive 扩展的局限性](#Appendix.PostgreSQL.CommonDBATasks.pgactive.requirements-limitations)
+ [初始化 pgactive 扩展功能](Appendix.PostgreSQL.CommonDBATasks.pgactive.basic-setup.md)
+ [为 RDS for PostgreSQL 数据库实例设置主动-主动复制](Appendix.PostgreSQL.CommonDBATasks.pgactive.setup-replication.md)
+ [衡量 pgactive 成员之间的复制延迟](Appendix.PostgreSQL.CommonDBATasks.pgactive.replicationlag.md)
+ [配置 pgactive 扩展的参数设置](Appendix.PostgreSQL.CommonDBATasks.pgactive.parameters.md)
+ [了解双活冲突](Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.replication.md)
+ [了解 pgactive 架构](Appendix.PostgreSQL.CommonDBATasks.pgactive.schema.md)
+ [pgactive 函数参考](pgactive-functions-reference.md)
+ [处理主动-主动复制中的冲突](Appendix.PostgreSQL.CommonDBATasks.pgactive.handle-conflicts.md)
+ [处理主动-主动复制中的序列](Appendix.PostgreSQL.CommonDBATasks.pgactive.handle-sequences.md)

## pgactive 扩展的局限性
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.requirements-limitations"></a>
+ 所有表都需要主键，否则不允许使用更新和删除。不应更新“主键”列中的值。
+ 序列可能存在间隙，有时可能不遵循顺序。不会复制序列。有关更多信息，请参阅 [处理主动-主动复制中的序列](Appendix.PostgreSQL.CommonDBATasks.pgactive.handle-sequences.md)。
+ 不会复制 DDL 和大型对象。
+ 辅助唯一索引可能会导致数据差异。
+ 组中所有节点上的排序规则需要相同。
+ 跨节点负载均衡是一种反模式。
+ 大型事务可能会导致复制滞后。

# 初始化 pgactive 扩展功能
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.basic-setup"></a>

要在 RDS for PostgreSQL 数据库实例上初始化 `pgactive` 扩展功能，请将参数 `rds.enable_pgactive` 的值设置为 `1`，然后在数据库中创建扩展。这样做会自动开启参数 `rds.logical_replication` 和 `track_commit_timestamp` 并将 `wal_level` 的值设置为 `logical`。

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

您可以使用 AWS 管理控制台或 AWS CLI 创建所需的 RDS for PostgreSQL 数据库实例。以下步骤假设您的 RDS for PostgreSQL 数据库实例与自定义数据库参数组相关联。有关创建自定义数据库参数组的信息，请参阅[Amazon RDS 的参数组](USER_WorkingWithParamGroups.md)。

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

**初始化 pgactive 扩展功能**

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

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

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

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

1. 找到 `rds.enable_pgactive` 参数，并将其设置为 `1` 以初始化 `pgactive` 功能。

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

1. 在 Amazon RDS 控制台的导航窗格中，选择**数据库**。

1. 选择您的 RDS for PostgreSQL 数据库实例，然后从**操作**菜单中选择**重启**。

1. 确认数据库实例重启，以便您的更改生效。

1. 当数据库实例可用时，您可以使用 `psql` 或任何其它 PostgreSQL 客户端连接到 RDS for PostgreSQL 数据库实例。

   以下示例假设 RDS for PostgreSQL 数据库实例有一个名为 *postgres* 的原定设置数据库。

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

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

   ```
   postgres=>SELECT setting ~ 'pgactive' 
   FROM pg_catalog.pg_settings
   WHERE name = 'shared_preload_libraries';
   ```

   如果 `pgactive` 在 `shared_preload_libraries` 中，则前面的命令将返回以下内容：

   ```
   ?column? 
   ----------
    t
   ```

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

**初始化 pgactive 扩展功能**

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

1. 使用以下 AWS CLI 命令将 `rds.enable_pgactive` 设置为 `1`，以初始化 RDS for PostgreSQL 数据库实例的 `pgactive` 功能。

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

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

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

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

   ```
   psql --host=mydb.111122223333.aws-region.rds.amazonaws.com --port=5432 --username=master user --password=PASSWORD --dbname=postgres
   ```

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

   ```
   postgres=>SELECT setting ~ 'pgactive' 
   FROM pg_catalog.pg_settings
   WHERE name = 'shared_preload_libraries';
   ```

   如果 `pgactive` 在 `shared_preload_libraries` 中，则前面的命令将返回以下内容：

   ```
   ?column? 
   ----------
    t
   ```

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

以下过程说明如何在 `pgactive` 可用的两个 RDS for PostgreSQL 数据库实例之间启动主动-主动复制。要运行多区域高可用性示例，您需要在两个不同的区域中部署 Amazon RDS for PostgreSQL 实例，并设置 VPC 对等。有关更多信息，请参阅 [VPC 对等](https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html)。

**注意**  
在多个区域之间发送流量可能会产生额外费用。

这些步骤假定已使用 `pgactive` 扩展启用 RDS for PostgreSQL 数据库实例。有关更多信息，请参阅 [初始化 pgactive 扩展功能](Appendix.PostgreSQL.CommonDBATasks.pgactive.basic-setup.md)。

**使用 `pgactive` 扩展配置第一个 RDS for PostgreSQL 数据库实例**

以下示例说明如何创建 `pgactive` 组，以及在 RDS for PostgreSQL 数据库实例上创建 `pgactive` 扩展所需的其它步骤。

1. 使用 `psql` 或其它客户端工具连接第一个 RDS for PostgreSQL 数据库实例。

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

1. 使用以下命令在 RDS for PostgreSQL 实例上创建数据库：

   ```
   postgres=> CREATE DATABASE app;
   ```

1. 使用以下命令将连接切换到新数据库：

   ```
   \c app
   ```

1. 使用以下 SQL 语句创建并填充示例表：

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

      ```
      app=> CREATE SCHEMA inventory;
      CREATE TABLE inventory.products (
      id int PRIMARY KEY, product_name text NOT NULL,
      created_at timestamptz NOT NULL DEFAULT CURRENT_TIMESTAMP);
      ```

   1. 使用以下 SQL 语句用一些示例数据填充表。

      ```
      app=> INSERT INTO inventory.products (id, product_name)
      VALUES (1, 'soap'), (2, 'shampoo'), (3, 'conditioner');
      ```

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

      ```
       app=>SELECT count(*) FROM inventory.products;
      
       count
      -------
       3
      ```

1. 在现有数据库上创建 `pgactive` 扩展。

   ```
   app=> CREATE EXTENSION pgactive;
   ```

1. 要安全地创建并初始化 pgactive 组，请使用以下命令：

   ```
   app=>
   -- connection info for endpoint1
   CREATE SERVER pgactive_server_endpoint1
       FOREIGN DATA WRAPPER pgactive_fdw
       OPTIONS (host '<endpoint1>', dbname 'app');
   CREATE USER MAPPING FOR postgres
       SERVER pgactive_server_endpoint1
       OPTIONS (user 'postgres', password '<password>');
         -- connection info for endpoint2
   CREATE SERVER pgactive_server_endpoint2
       FOREIGN DATA WRAPPER pgactive_fdw
       OPTIONS (host '<endpoint2>', dbname 'app');
   CREATE USER MAPPING FOR postgres
       SERVER pgactive_server_endpoint2
       OPTIONS (user 'postgres', password '<password>');
   ```

   现在，您可以初始化复制组并添加此第一个实例：

   ```
   SELECT pgactive.pgactive_create_group(
       node_name := 'endpoint1-app',
       node_dsn := 'user_mapping=postgres pgactive_foreign_server=pgactive_server_endpoint1'
   
   );
   ```

   使用以下命令作为替代方案（安全性较低）来创建并初始化 pgactive 组：

   ```
   app=> SELECT pgactive.pgactive_create_group(
       node_name := 'node1-app',
       node_dsn := 'dbname=app host=firstinstance.111122223333.aws-region.rds.amazonaws.com user=postgres password=PASSWORD');
   ```

   node1-app 是您分配的名称，用于唯一标识 `pgactive` 组中的节点。
**注意**  
要在可公开访问的数据库实例上成功执行此步骤，必须通过将 `rds.custom_dns_resolution` 参数设置为 `1` 将其开启。

1. 要检查数据库实例是否就绪，请使用以下命令：

   ```
   app=> SELECT pgactive.pgactive_wait_for_node_ready();
   ```

   如果命令成功，您可以看到以下输出：

   ```
   pgactive_wait_for_node_ready 
   ------------------------------ 
   (1 row)
   ```

**配置第二个 RDS for PostgreSQL 实例并将其加入 `pgactive` 组**

以下示例说明如何将 RDS for PostgreSQL 数据库实例加入 `pgactive` 组，以及在数据库实例上创建 `pgactive` 扩展所需的其它步骤。

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

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

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

1. 使用以下命令在第二个 RDS for PostgreSQL 数据库实例上创建数据库：

   ```
   postgres=> CREATE DATABASE app;
   ```

1. 使用以下命令将连接切换到新数据库：

   ```
   \c app
   ```

1. 在现有数据库上创建 `pgactive` 扩展。

   ```
   app=> CREATE EXTENSION pgactive;
   ```

1. 使用以下命令以更安全的方式将 RDS for PostgreSQL 第二个数据库实例加入 `pgactive` 组：

   ```
   -- connection info for endpoint1
   CREATE SERVER pgactive_server_endpoint1
       FOREIGN DATA WRAPPER pgactive_fdw
       OPTIONS (host '<endpoint1>', dbname 'app');
   CREATE USER MAPPING FOR postgres
       SERVER pgactive_server_endpoint1
       OPTIONS (user 'postgres', password '<password>');
   
   -- connection info for endpoint2
   CREATE SERVER pgactive_server_endpoint2
       FOREIGN DATA WRAPPER pgactive_fdw
       OPTIONS (host '<endpoint2>', dbname 'app');
   CREATE USER MAPPING FOR postgres
       SERVER pgactive_server_endpoint2
       OPTIONS (user 'postgres', password '<password>');
   ```

   ```
   SELECT pgactive.pgactive_join_group(
       node_name := 'endpoint2-app',
       node_dsn := 'user_mapping=postgres pgactive_foreign_server=pgactive_server_endpoint2',
       join_using_dsn := 'user_mapping=postgres pgactive_foreign_server=pgactive_server_endpoint1'
   );
   ```

   使用以下命令作为替代方案（安全性较低）将 RDS for PostgreSQL 第二个数据库实例加入 `pgactive` 组

   ```
   app=> SELECT pgactive.pgactive_join_group(
   node_name := 'node2-app',
   node_dsn := 'dbname=app host=secondinstance.111122223333.aws-region.rds.amazonaws.com user=postgres password=PASSWORD',
   join_using_dsn := 'dbname=app host=firstinstance.111122223333.aws-region.rds.amazonaws.com user=postgres password=PASSWORD');
   ```

   node2-app 是您分配的名称，用于唯一标识 `pgactive` 组中的节点。

1. 要检查数据库实例是否就绪，请使用以下命令：

   ```
   app=> SELECT pgactive.pgactive_wait_for_node_ready(); 
   ```

   如果命令成功，您可以看到以下输出：

   ```
   pgactive_wait_for_node_ready 
   ------------------------------ 
   (1 row)
   ```

   如果第一个 RDS for PostgreSQL 数据库相对较大，则可以看到 `pgactive.pgactive_wait_for_node_ready()` 正在发出还原操作的进度报告。输出看上去类似于以下内容：

   ```
   NOTICE:  restoring database 'app', 6% of 7483 MB complete
   NOTICE:  restoring database 'app', 42% of 7483 MB complete
   NOTICE:  restoring database 'app', 77% of 7483 MB complete
   NOTICE:  restoring database 'app', 98% of 7483 MB complete
   NOTICE:  successfully restored database 'app' from node node1-app in 00:04:12.274956
    pgactive_wait_for_node_ready 
   ------------------------------ 
   (1 row)
   ```

   此后，`pgactive` 将在两个数据库实例之间同步数据。

1. 您可以使用以下命令来验证第二个数据库实例的数据库是否具有数据：

   ```
   app=> SELECT count(*) FROM inventory.products;
   ```

   如果数据成功同步，您将看到以下输出：

   ```
    count
   -------
    3
   ```

1. 运行以下命令插入新值：

   ```
   app=> INSERT INTO inventory.products (id, product_name) VALUES (4, 'lotion');
   ```

1. 连接到第一个数据库实例的数据库并运行以下查询：

   ```
   app=> SELECT count(*) FROM inventory.products;
   ```

   如果已初始化主动-主动复制，则输出类似于以下内容：

   ```
   count
   -------
    4
   ```

**从 `pgactive` 组中分离和移除数据库实例**

您可以使用以下步骤从 `pgactive` 组中分离和移除数据库实例：

1. 您可以使用以下命令将第二个数据库实例与第一个数据库实例分离：

   ```
   app=> SELECT * FROM pgactive.pgactive_detach_nodes(ARRAY[‘node2-app']);
   ```

1. 使用以下命令从第二个数据库实例中移除 `pgactive` 扩展：

   ```
   app=> SELECT * FROM pgactive.pgactive_remove();
   ```

   要强制移除此扩展，请执行以下操作：

   ```
   app=> SELECT * FROM pgactive.pgactive_remove(true);
   ```

1. 使用以下命令删除扩展：

   ```
   app=> DROP EXTENSION pgactive;
   ```

# 衡量 pgactive 成员之间的复制延迟
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.replicationlag"></a>

您可以使用以下查询来查看 `pgactive` 成员之间的复制延迟。在每个 `pgactive` 节点上运行此查询以了解全部信息。

```
    
app=> SELECT * FROM pgactive.pgactive_get_replication_lag_info();
│-[ RECORD 1 ]--------+---------------------------------------------
│node_name            | node2-app
│node_sysid           | 7481018224801653637
│application_name     | pgactive:7481018224801653637:send
│slot_name            | pgactive_16385_7481018224801653637_0_16385__
│active               | t
│active_pid           | 783486
│pending_wal_decoding | 0
│pending_wal_to_apply | 0
│restart_lsn          | 0/2108150
│confirmed_flush_lsn  | 0/2154690
│sent_lsn             | 0/2154690
│write_lsn            | 0/2154690
│flush_lsn            | 0/2154690
│replay_lsn           | 0/2154690
│-[ RECORD 2 ]--------+---------------------------------------------
│node_name            | node1-app
│node_sysid           | 7481018033434600853
│application_name     | pgactive:7481018033434600853:send
│slot_name            | pgactive_16385_7481018033434600853_0_16385__
│active               | t
│active_pid           | 783488
│pending_wal_decoding | 0
│pending_wal_to_apply | 0
│restart_lsn          | 0/20F5AD0
│confirmed_flush_lsn  | 0/214EF68
│sent_lsn             | 0/214EF68
│write_lsn            | 0/214EF68
│flush_lsn            | 0/214EF68
│replay_lsn           | 0/214EF68
```

至少需要监控以下诊断信息：

active  
在 active 为 false 时设置警报，这表示该插槽当前未被使用（订阅用户实例已断开与发布者的连接）。

pending\$1wal\$1decoding  
在 PostgreSQL 的逻辑复制中，WAL 文件以二进制格式存储。发布者必须解码这些 WAL 更改并将其转换为逻辑更改（例如插入、更新或删除操作）。  
指标 pending\$1wal\$1decoding 显示发布者端等待解码的 WAL 文件的数量。  
该数量可能因以下因素增大：  
+ 当未连接订阅用户时，active 状态将为 false，并且 pending\$1wal\$1decoding 将增大
+ 该插槽处于 active 状态，但发布者无法跟上 WAL 更改的量

pending\$1wal\$1to\$1apply  
指标 pending\$1wal\$1apply 表示订阅用户端等待应用的 WAL 文件的数量。  
有以下几个因素可能会阻止订阅用户应用更改，并且可能导致出现磁盘已满的情况：  
+ 架构差异 – 例如，当您在名为 sample 的表的 WAL 流中做出了更改，但订阅用户端却不存在该表时
+ 主键列中的值已更新
+ 辅助唯一索引可能会导致数据差异

# 配置 pgactive 扩展的参数设置
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.parameters"></a>

您可以使用以下查询来查看与 `pgactive` 扩展关联的所有参数。

```
app=> SELECT * FROM pg_settings WHERE name LIKE 'pgactive.%';
```

您可以使用各种参数配置 `pgactive` 扩展。可通过 AWS 管理控制台或 AWS CLI 界面设置这些参数。

## pgactive 扩展的主要参数
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.mainparams"></a>

下表提供了 `pgactive` 扩展的主要参数的参考：


| 参数 | 单位 | 默认值 | 说明 | 
| --- | --- | --- | --- | 
| pgactive.conflict\$1logging\$1include\$1tuples | `boolean` | –  | 记录 `pgactive` 扩展的完整元组信息。  要使更改生效，必须先重启服务器。  | 
| pgactive.log\$1conflicts\$1to\$1table | `boolean` | –  | 确定 `pgactive` 扩展是否已将检测到的冲突记录到 `pgactive.pgactive_conflict_history` 表中。有关更多信息，请参阅冲突日志记录。  要使更改生效，必须先重启服务器。  | 
| pgactive.log\$1conflicts\$1to\$1logfile | `boolean` | –  | 确定 `pgactive` 扩展是否已将检测到的冲突记录到 PostgreSQL 日志文件中。有关更多信息，请参阅冲突日志记录。  要使更改生效，必须先重启服务器。  | 
| pgactive.synchronous\$1commit | `boolean` | off | 确定 pgactive 应用工作线程的提交行为。在此设置处于禁用（off）状态时，应用工作线程会执行异步提交，这将提高 PostgreSQL 在应用操作期间的吞吐量，但会延迟向上游发送的重播确认。将此设置设为 `off` 始终是安全的，不会导致事务丢失或跳过。此设置仅影响下游节点上的磁盘刷新时间以及向上游发送确认的时间。系统会延迟发送重播刷新确认，直到通过检查点或定期工作等不相关的操作将提交刷新到磁盘。但是，如果上游具有 `synchronous_standby_names` 中列出的下游，则将此设置设定为 `off` 会导致上游的同步提交需要更长的时间才能向客户端返回成功响应。在此情况下，可将该参数设置为 `on`。  即使将该参数设置为 `on` 并在 `synchronous_standby_names` 中列出了节点，主动-主动配置中也仍然可能发生复制冲突。这是因为系统缺少节点间锁定和全局快照管理，可能导致不同节点上的并发事务修改同一元组。此外，事务仅在上游节点上提交后才开始复制。启用同步提交并不会将 pgactive 扩展转换为始终一致的系统。  | 
| pgactive.temp\$1dump\$1directory | `string` | – | 定义数据库克隆操作在初始设置期间所需的临时存储路径。此目录必须可由 postgres 用户写入，并且拥有足够的存储空间来容纳完整的数据库转储。系统仅在通过逻辑复制操作进行初始数据库设置期间使用此位置。`pgactive_init_copy command` 不使用该参数。 | 
| pgactive.max\$1ddl\$1lock\$1delay | `milliseconds` | `-1` | 指定在强制中止并发写入事务之前，DDL 锁的最长等待时间。默认值为 `-1`，这将使用 `max_standby_streaming_delay` 中设置的值。该参数接受时间单位。例如，可以将它设置为 10s 来表示 10 秒。在此等待期间，系统会尝试获取 DDL 锁，同时等待正在进行的写入事务完成提交或回滚。有关更多信息，请参阅“DDL 锁定”。 | 
| pgactive.ddl\$1lock\$1timeout | `milliseconds` | `-1` | 指定 DDL 锁为获取锁而尝试等待的时长。默认值为 `-1`，这将使用 lock\$1timeout 中指定的值。您可以使用时间单位来设置此参数，例如 10s 表示 10 秒。此计时器仅控制为获取 DDL 锁而等待的时长。在系统获取锁并开始 DDL 操作后，计时器将停止。此参数不会限制可将 DDL 锁保持的总持续时间或总 DDL 操作时间。要控制操作的总持续时间，请改用 `statement_timeout`。有关更多信息，请参阅“DDL 锁定”。 | 
| pgactive.debug\$1trace\$1ddl\$1locks\$1level | `boolean` | –  | 覆盖 `pgactive` 扩展中 DDL 锁定操作的默认调试日志级别。配置此设置后，会导致系统在 LOG 调试级别而非默认级别发出与 DDL 锁相关的消息。使用此参数可监控 DDL 锁定活动，而无需在整个服务器上启用详细 `DEBUG1` 或 `DEBUG2` 日志级别。 可用日志级别（按详细程度升序排列）： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.pgactive.parameters.html) 有关监控选项的更多信息，请参阅“监控全局 DDL 锁”。  对此设置所做的更改会在重新加载配置后生效。您无需重新启动服务器。   | 

## pgactive 扩展的其他参数
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.addparams"></a>

下表列出了 `pgactive` 扩展中不常用的配置选项和可用的内部配置选项。


| 参数 | 单位 | 默认值 | 说明 | 
| --- | --- | --- | --- | 
| pgactive.debug\$1apply\$1delay | `integer` | – |  为 `pgactive.pgactive_connections` 条目中未明确设定应用延迟的已配置连接设置应用延迟（以毫秒为单位）。此延迟是在节点创建或加入时设置的，pgactive 不会在对等节点上重播事务，直到提交后至少经过指定的毫秒数。 主要用于在测试环境中模拟高延迟网络，以便更轻松地制造冲突。例如，如果在节点 A 和 B 上设置了 500 毫秒的延迟，则在节点 A 上插入一个值后，至少为您提供了 500 毫秒的时间在节点 B 上执行冲突插入。  需要重新加载服务器或重新启动应用工作线程，设置才能生效。  | 
| pgactive.connectability\$1check\$1duration | `integer` | –  | 指定数据库工作线程在尝试连接失败后，继续尝试建立连接的持续时间（以秒为单位）。工作线程每秒尝试连接一次，直到连接成功或达到此超时值。当数据库引擎在工作线程准备好建立连接之前启动时，此设置很有用。 | 
| pgactive.skip\$1ddl\$1replication | `boolean` | `on` | 控制如何在启用 `pgactive` 时，在 Amazon RDS 中复制或处理 DDL 更改。在设置为 `on` 时，该节点会像非 pgactive 节点一样处理 DDL 更改。在使用此参数时，需符合以下要求： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.pgactive.parameters.html) 您可以使用超级用户权限以全局和本地（会话级别）这两种方式修改此参数。  错误地更改此参数可能会中断您的复制设置。  | 
| pgactive.do\$1not\$1replicate | `boolean` | – | 此参数仅供内部使用。在事务中设置此参数时，所做的更改不会复制到数据库集群内的其他节点。  错误地更改此参数可能会中断您的复制设置。  | 
| pgactive.discard\$1mismatched\$1row\$1attributes | `boolean` | –  | 此参数仅供专业人员使用。建议仅在解决特定的复制问题时使用此参数。在以下情况下可使用此参数： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.pgactive.parameters.html) 此设置会覆盖以下错误消息，并允许产生数据差异，以便让复制进程持续进行：`cannot right-pad mismatched attributes; attno %u is missing in local table and remote row has non-null, non-dropped value for this attribute`  错误地更改此参数可能会中断您的复制设置。   | 
| pgactive.debug\$1trace\$1replay | `boolean` | – | 在设置为 `on` 时，它会为下游应用工作线程处理的每个远程操作发出一条日志消息。日志包括： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.pgactive.parameters.html) 日志还会捕获已排队的 DDL 命令和表删除事件。para> 默认情况下，日志不包括行字段内容。要在日志中包含行值，您必须在启用以下标志的情况下重新编译： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.pgactive.parameters.html)  启用此日志记录设置可能会影响性能。建议仅在需要此设置来进行问题排查时启用它。对此设置所做的更改会在重新加载配置后生效。您无需重新启动服务器。   | 
| pgactive.extra\$1apply\$1connection\$1options |  | – | 您可以为所有与 pgactive 节点的对等节点连接配置连接参数。这些参数可控制 keepalives 和 SSL 模式等设置。默认情况下，pgactive 使用以下连接参数： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.pgactive.parameters.html) 要覆盖默认参数，请使用以下类似命令： pgactive.extra\$1apply\$1connection\$1options = 'keepalives=0' 单个节点连接字符串优先于这些设置和 pgactive 的内置连接选项。有关连接字符串格式的更多信息，请参阅 [libpq connection strings](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING)。 我们建议将默认的 keepalive 设置保持启用状态。仅在您通过不可靠的网络完成大量事务时遇到问题的情况下，才禁用 keepalives。  我们建议将默认的 keepalive 设置保持启用状态。仅在您通过不可靠的网络完成大量事务时遇到问题的情况下，才禁用 keepalives。对此设置所做的更改会在重新加载配置后生效。您无需重新启动服务器。  | 
| pgactive.init\$1node\$1parallel\$1jobs (int) |  | – | 指定在逻辑节点与 `pgactive.pgactive_join_group` 函数联接期间，`pg_dump` 和 `pg_restore` 可使用的并行作业数。 对此设置所做的更改会在重新加载配置后生效。您无需重新启动服务器。 | 
| pgactive.max\$1nodes | `int` | 4 |  指定 pgactive 扩展组中可包含的节点的最大数目。默认值为 4 个节点。在设置此参数的值时，必须注意以下几点： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_cn/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.pgactive.parameters.html) 可通过两种方式设置此参数，即在配置文件中进行设置或者使用 `ALTER SYSTEM SET` 命令进行设置 此参数的默认值为 `4`，这意味着，`pgactive` 扩展组始终最多只能包含 4 个节点。  重新启动服务器后，此更改将生效。  | 
| pgactive.permit\$1node\$1identifier\$1getter\$1function\$1creation | `boolean` | – | 此参数仅供内部使用。启用后，`pgactive` 扩展将允许创建 pgactive 节点标识符 getter 函数。 | 

# 了解双活冲突
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.replication"></a>

如果您在双活模式下使用 pgactive，在从多个节点写入同一个表时可能会造成数据冲突。一些集群化系统会使用分布式锁来防止并发访问，不过 pgactive 采用的是乐观方法，更适合分布在不同地理位置的应用程序。

一些数据库集群系统使用分布式锁来防止并发数据访问。但是，此方法只适合距离非常近的服务器，不支持分布在不同地理位置的应用程序，因为此方法需要极低的延迟才能实现良好的性能。pgactive 扩展没有使用分布式锁（这是一种悲观方法），而是使用乐观方法。这意味着：
+ 可帮助您尽可能避免冲突。
+ 允许出现某些类型的冲突。
+ 发生冲突时会提供冲突解决方案。

通过这种方法，您在构建分布式应用程序时具有更好的灵活性。

## 发生冲突的原因
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.howconflicts"></a>

节点间冲突是由事件序列引起的，如果涉及的所有事务同时发生在同一个节点上，则不会出现节点间冲突。由于节点仅在事务提交后交换更改，因此每个事务在其提交的节点上都是分别有效的，但如果事务在另一个同时完成其他工作的节点上运行，就会无效。由于 pgactive 的应用操作本质上是在其他节点上重播事务，因此，如果正在应用的事务与在接收节点上提交的事务之间存在冲突，则重播操作会失败。

 当所有事务都运行在单个节点上时，大多数冲突不会发生的原因是 PostgreSQL 采用了事务间通信机制来防止冲突，包括：
+ UNIQUE 索引
+ SEQUENCE
+ 行和关系锁定
+ SERIALIZABLE 依赖关系跟踪

所有这些机制都是在事务之间进行通信的方法，用于防止出现意外并发问题

pgactive 不使用分布式事务管理器或锁管理器，因此可以实现低延迟并很好地处理网络分区。但是，这意味着不同节点上事务的运行是彼此完全隔离的。虽然隔离通常可以提高数据库的一致性，但在这种情况下，您需要减少隔离以防止冲突。

## 冲突类型
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflicttypes"></a>

可能发生的冲突包括：

**Topics**
+ [PRIMARY KEY 或 UNIQUE 冲突](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict1)
+ [INSERT/INSERT 冲突](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict2)
+ [违反多个 UNIQUE 约束的 INSERT](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict3)
+ [UPDATE/UPDATE 冲突](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict4)
+ [PRIMARY KEY 上的 UPDATE 冲突](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict5)
+ [违反多个 UNIQUE 约束的 UPDATE](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict6)
+ [UPDATE/DELETE 冲突](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict7)
+ [INSERT/UPDATE 冲突](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict8)
+ [DELETE/DELETE 冲突](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict9)
+ [外键约束冲突](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict10)
+ [排除约束冲突](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict11)
+ [全局数据冲突](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict12)
+ [锁冲突和死锁中止](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict13)
+ [分歧冲突](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict14)

### PRIMARY KEY 或 UNIQUE 冲突
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict1"></a>

当多个操作试图以单个节点上不可能出现的方式修改同一个行键时，就会发生行冲突。这些冲突体现了最常见的数据冲突类型。

pgactive 采用上次更新获胜处理方法或者您的自定义冲突处理程序，来解决检测到的冲突。

行冲突包括：
+ INSERT 与 INSERT
+ INSERT 与 UPDATE
+ UPDATE 与 DELETE
+ INSERT 与 DELETE
+ DELETE 与 DELETE
+ INSERT 与 DELETE

### INSERT/INSERT 冲突
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict2"></a>

这是最常见的冲突，当两个不同节点上的 INSERT 操作创建具有相同 PRIMARY KEY 值的元组（或在没有 PRIMARY KEY 时，使用相同的 UNIQUE 约束值），就会发生这种冲突。

pgactivelink 使用来自原始主机的时间戳来保留最新的元组，以此解决 INSERT 冲突。您可以使用自定义冲突处理程序覆盖此默认行为。虽然此过程不需要管理员专门采取操作，但请注意，pgactivelink 会丢弃所有节点上的其中一个 INSERT 操作。除非您的自定义处理程序实施自动数据合并，否则不会自动进行数据合并。

pgactivelink 只能解决涉及单个约束违规的冲突。如果某个 INSERT 违反了多个 UNIQUE 约束，您必须实施其他冲突解决策略。

### 违反多个 UNIQUE 约束的 INSERT
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict3"></a>

INSERT/INSERT 冲突可能违反多个 UNIQUE 约束，包括 PRIMARY KEY。pgactivelink 只能处理涉及单个 UNIQUE 约束的冲突。当冲突违反多个 UNIQUE 约束时，应用工作线程会失败并返回以下错误：

`multiple unique constraints violated by remotely INSERTed tuple.`

在较早的版本中，这种情况生成的是“分歧唯一性冲突”错误。

要解决这些冲突，您必须手动采取操作。您可以对发生冲突的本地元组执行 DELETE，或者对元组执行 UPDATE 以使用新的远程元组来消除冲突。请注意，您可能需要解决多个相互冲突的元组。目前，pgactivelink 没有提供内置功能用于忽略、丢弃或合并违反多个唯一约束的元组。

**注意**  
有关更多信息，请参阅“违反多个 UNIQUE 约束的 UPDATE”。

### UPDATE/UPDATE 冲突
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict4"></a>

当两个节点并发修改同一个元组而没有更改其 PRIMARY KEY 时，就会发生此冲突。pgactivelink 使用上次更新获胜逻辑或您的自定义冲突处理程序（如果已定义）来解决这些冲突。PRIMARY KEY 对于元组匹配和冲突解决至关重要。对于没有 PRIMARY KEY 的表，pgactivelink 拒绝 UPDATE 操作，并显示以下错误：

`Cannot run UPDATE or DELETE on table (tablename) because it does not have a primary key.`

### PRIMARY KEY 上的 UPDATE 冲突
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict5"></a>

pgactive 在处理 PRIMARY KEY 更新时存在限制。虽然您可以对 PRIMARY KEY 执行 UPDATE 操作，但对于这些操作，pgactive 无法使用最后更新获胜逻辑自动解决冲突。您必须确保 PRIMARY KEY 更新不会与现有值冲突。如果在 PRIMARY KEY 更新期间发生冲突，这些冲突就会成为分歧冲突，需要您手动干预。有关处理这些情况的更多信息，请参阅[分歧冲突](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict14)。

### 违反多个 UNIQUE 约束的 UPDATE
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict6"></a>

当传入的 UPDATE 违反多个 UNIQUE 约束或 PRIMARY KEY 值时，pgactivelink 无法应用上次更新获胜冲突解决方案。此行为类似于出现多个约束冲突的 INSERT 操作。这些情况会造成分歧冲突，需要您手动干预。有关更多信息，请参阅[分歧冲突](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict14)。

### UPDATE/DELETE 冲突
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict7"></a>

当一个节点对一行执行 UPDATE 而另一个节点同时对该行执行 DELETE 时，就会出现此冲突。在这种情况下，重播时会发生 UPDATE/DELETE 冲突。除非您的自定义冲突处理程序另行规定，否则解决方案是丢弃在 DELETE 之后到达的所有 UPDATE。

pgactivelink 需要一个 PRIMARY KEY 来匹配元组并解决冲突。对于没有 PRIMARY KEY 的表，表会拒绝 DELETE 操作，并显示以下错误：

`Cannot run UPDATE or DELETE on table (tablename) because it does not have a primary key.`

**注意**  
pgactivelink 无法区分 UPDATE/DELETE 和 INSERT/UPDATE 冲突。在这两种情况下，UPDATE 均会影响不存在的行。由于异步复制以及节点之间缺乏重播排序，pgactivelink 无法确定是在对新行（尚未收到 INSERT）还是对已删除行执行 UPDATE 操作。在这两种情况下，pgactivelink 都会丢弃 UPDATE。

### INSERT/UPDATE 冲突
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict8"></a>

这种冲突可能发生在多节点环境中。当一个节点 INSERT 一行数据，第二个节点对该行执行 UPDATE，而第三个节点在收到原始 INSERT 之前就接收到 UPDATE 时，就会发生这种情况。默认情况下，除非您的自定义冲突触发器另行规定，否则 pgactivelink 会通过放弃 UPDATE 来解决这些冲突。请注意，这种解决方法可能会导致节点间的数据不一致。有关类似场景及其处理方法的更多信息，请参阅 [UPDATE/DELETE 冲突](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict7)。

### DELETE/DELETE 冲突
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict9"></a>

当两个不同的节点并发删除同一个元组时，就会发生这种冲突。pgactivelink 认为这些冲突无害，因为两个 DELETE 操作会产生相同的最终结果。在此场景中，pgactivelink 可以安全地忽略其中一个 DELETE 操作，而不会影响数据一致性。

### 外键约束冲突
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict10"></a>

对现有本地数据应用远程事务时，FOREIGN KEY 约束可能会导致冲突。发生这些冲突的情况通常是应用事务的顺序，不同于事务在发起节点上的逻辑顺序。

默认情况下，pgactive 会将 session\$1replication\$1role 作为 `replica` 来应用更改，这会在复制过程中绕过外键检查。在双活配置中，这可能会导致外键违规。大多数违规行为都是暂时性的，当复制进度跟上时就会解决。但是，由于 pgactive 不支持跨节点行锁定，因此可能会出现悬挂外键。

这种行为是具备分区容错性的异步双活系统所固有的。例如，节点 A 可能会插入新的子行，而节点 B 同时在删除其父行。系统无法阻止节点之间这种类型的并发修改。

为尽可能减少外键冲突，我们给出了以下建议：
+ 将外键关系限制为密切相关的实体。
+ 尽可能从单个节点修改相关实体。
+ 选择极少需要进行修改的实体。
+ 针对修改实施应用程序级的并发控制。

### 排除约束冲突
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict11"></a>

 pgactivelink 不支持排除约束，并会限制创建这种约束。

**注意**  
在将现有的独立数据库转换为 pgactivelink 数据库时，请手动删除所有排除约束。

在分布式异步系统中，不可能保证所有行集都没有违反约束。这是因为不同节点上的所有事务都是完全隔离的。排除约束可能导致重播死锁，在这种情况下，由于排除约束违规，重播无法从任何节点进展到另一个节点。

如果您强制 pgactivelink 创建排除约束，或者在将独立数据库转换到 pgactivelink 时不删除现有排除约束，则复制很可能会中断。要恢复复制进度，请删除或更改与传入的远程元组冲突的本地元组，这样便可以应用远程事务。

### 全局数据冲突
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict12"></a>

使用 pgactivelink 时，当节点具有不同的全局 PostgreSQL 系统级数据（例如角色）时，就可能会发生冲突。这些冲突的结果是操作（主要是 DDL）会成功并在一个节点上提交，但无法应用于其他节点。

如果一个节点上有某个用户但另一个节点上没有，就会出现复制问题：
+ Node1 有名为 `fred` 的用户，但是 Node2 上没有该用户
+ 当 `fred` 在 Node1 上创建表时，表在复制时的所有者是 `fred`
+ 将此 DDL 命令应用于 Node2 时，命令会失败，因为用户 `fred` 不存在
+ 在失败后，会在 Node2 上的 PostgreSQL 日志中生成 ERROR 条目并递增 `pgactive.pgactive_stats.nr_rollbacks` 计数器

**解决方案：**在 Node2 上创建用户 `fred`。该用户不必具备相同的权限，但必须存在于两个节点上。

如果一个节点上有某个表但另一个节点上没有，则数据修改操作会失败：
+ Node1 上有名为 `foo` 的表，但 Node2 上没有
+ Node1 上对 `foo` 表执行的任何 DML 操作，在复制到 Node2 时会失败

**解决方案：**在 Node2 上创建具有相同结构的表 `foo`。

**注意**  
pgactivelink 目前不复制 CREATE USER 命令或 DDL 操作。DDL 复制功能计划在未来版本中发布。

### 锁冲突和死锁中止
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict13"></a>

由于 pgactive 应用进程的运行方式类似于普通用户会话，因此这些进程遵循标准的行和表锁定规则。这可能会导致 pgactivelink 应用进程需要等待用户事务或其他应用进程持有的锁被释放。

以下类型的锁会影响应用进程：
+ 用户会话施加的显式表级锁（LOCK TABLE...）
+ 用户会话施加的显式行级锁（SELECT ... FOR UPDATE/FOR SHARE）
+ 来自外键的锁定
+ 由于行 UPDATE、INSERT 或 DELETE 而导致的隐式锁定，这可能源自本地活动，也可能是从其他服务器应用的

在下列操作之间可能会出现死锁：
+ pgactivelink 应用进程和用户事务
+ 两个应用进程

发生死锁时，PostgreSQL 的死锁检测器会终止其中一个出现问题的事务。如果 pgactivelink 应用工作线程的进程终止，则会自动重试而且通常会成功。

**注意**  
这些问题是暂时性的，通常无需管理员干预。如果由于空闲用户会话锁定导致某个应用进程被长时间阻止，您可以终止该用户会话来恢复复制。这种情况类似于某个用户持有的长时间锁定影响到了其他用户会话。
要识别与锁定相关的重播延迟，请在 PostgreSQL 中启用 `log_lock_waits` 功能。

### 分歧冲突
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict14"></a>

当各节点之间本应相同的数据意外地变得不同时，就会发生分歧冲突。虽然这些冲突不应该发生，但在当前的实施中，无法可靠地预防所有冲突。

**注意**  
 在一行上，如果有某个节点在所有节点处理更改之前更改该行的主键，则修改行的 PRIMARY KEY 会导致分歧冲突。请避免更改主键，或将更改限制在某个指定节点上。有关更多信息，请参阅 [PRIMARY KEY 上的 UPDATE 冲突](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict5)。

涉及行数据的分歧冲突通常需要管理员干预。要解决这些冲突，您必须手动调整一个节点上的数据来与其他节点相匹配，同时使用 `pgactive.pgactive_do_not_replicate` 暂时禁用复制。如果您按照文档说明使用 pgactive 并避免使用标记为不安全的设置或函数，则不应出现这些冲突。

 作为管理员，您必须手动解决这些冲突。根据冲突类型，您需要使用诸如 `pgactive.pgactive_do_not_replicate` 之类的高级选项。请谨慎使用这些选项，因为使用不当会导致情况恶化。由于可能出现的冲突各种各样，我们无法提供通用的解决方案说明。

当不同节点之间本应相同的数据意外地变得不同时，就会发生分歧冲突。虽然这些冲突不应该发生，但在当前的实施中，无法可靠地预防所有此类冲突。

## 避免或容忍冲突
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.avoidconflicts"></a>

 在大多数情况下，您可以使用合适的应用程序设计，从而避免冲突或使应用程序具备容忍冲突的能力。

 只有在多个节点上同时进行多个操作时才会发生冲突。为避免冲突，请采取以下方法：
+ 仅向一个节点写入
+ 写入各个节点上的独立数据库子集（例如，向每个节点分配一个单独的架构）

对于 INSERT 与 INSERT 冲突，请使用全局序列来彻底防止冲突。

 如果您的应用场景不能接受冲突，请考虑在应用程序级别实施分布式锁定。通常，最好的方法是对应用程序进行设计，使其与 pgactive 的冲突解决机制配合使用，而不是尝试预防所有冲突。有关更多信息，请参阅 [冲突类型](#Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflicttypes)。

## 冲突日志记录
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflictlogging"></a>

pgactivelink 会将冲突事件记录到 `pgactive.pgactive_conflict_history` 表中，供您诊断和处理双活冲突。仅当 `pgactive.log_conflicts_to_table` 设置为 true 时，才会将冲突记录到此表。无论 `pgactive.log_conflicts_to_table` 的设置如何，当 log\$1min\$1messages 设置为 `LOG` 或 `lower` 时，pgactive 扩展还会将冲突记录到 PostgreSQL 日志文件中。

 使用冲突历史记录表可以：
+ 衡量您的应用程序造成冲突的频率
+ 确定在什么位置发生冲突
+ 改进应用程序以降低冲突率
+ 检测冲突解决方案无法达成所需结果的情况
+ 确定哪些地方需要用户定义的冲突触发器或更改应用程序设计

 对于行冲突，您可以选择记录行值。这由 `pgactive.log_conflicts_to_table` 设置控制。请注意：
+ 这是一个全局数据库级选项
+ 无法按各个表来控制对行值的记录
+ 字段数量、数组元素或字段长度没有限制
+ 如果您在处理数 MB 的行而这些行可能会触发冲突，则不建议启用此功能

 由于冲突历史记录表包含来自数据库中各个表的数据（每个表都可能采用不同架构），因此记录的行值存储为 JSON 字段。JSON 使用 `row_to_json` 创建，类似于直接从 SQL 中调用。PostgreSQL 不提供 `json_to_row` 函数，因此您需要特定于表的代码（采用 PL/pgSQL、PL/Python、PL/Perl 等）才能从记录的 JSON 中重新构造复合类型的元组。

**注意**  
对用户定义冲突的支持计划作为未来的扩展功能推出。

# 了解 pgactive 架构
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.schema"></a>

pgactive 架构管理 RDS for PostgreSQL 中的主动-主动复制。此架构包含用于存储复制配置和状态信息的表。

**注意**  
pgactive 架构正在演进中，可能会发生变更。不要直接修改这些表中的数据。

pgactive 架构中的键表包括：
+ `pgactive_nodes` – 存储有关主动-主动复制组中的节点的信息。
+ `pgactive_connections` – 存储每个节点的连接详细信息。

## pgactive\$1nodes
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.schema.nodes"></a>

pgactive\$1nodes 存储有关参与主动-主动复制组的节点的信息。


| 列 | 类型 | 排序规则 | 是否可为 null | 默认值 | 
| --- | --- | --- | --- | --- | 
| node\$1sysid | 文本 | – | 不为 null | – | 
| node\$1timeline | oid | – | 不为 null | – | 
| node\$1dboid | oid | – | 不为 null | – | 
| node\$1status | char | – | 不为 null | – | 
| node\$1name | 文本 | – | 不为 null | – | 
| node\$1dsn | 文本 | – | 不为 null | – | 
| node\$1init\$1from\$1dsn | 文本 | – | 不为 null | – | 
| node\$1read\$1only | 布尔值 | – | – | false | 
| node\$1seq\$1id | smallint | – | 不为 null | – | 

**node\$1sysid**  
在 `pgactive_create_group` 或 `pgactive_join_group` 期间生成的节点的唯一 ID

**node\$1status**  
节点的准备情况：  
+ **b** – 开始设置
+ **i** – 正在初始化
+ **c** – 追赶
+ **o** – 创建出站插槽
+ **r** – 已就绪
+ **k** – 已终止
此列不指明节点已连接还是已断开连接。

**node\$1name**  
用户提供的唯一节点名称。

**node\$1dsn**  
连接字符串或用户映射名称。

**node\$1init\$1from\$1dsn**  
从中创建了此节点的 DSN。

## pgactive\$1connection
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.schema.connection"></a>

pgactive\$1connections 存储每个节点的连接详细信息。


| 列 | 类型 | 排序规则 | 是否可为 null | 默认值 | 
| --- | --- | --- | --- | --- | 
| conn\$1sysid | 文本 | none | 不为 null | none | 
| conn\$1timeline | oid | none | 不为 null | none | 
| conn\$1dboid | oid | none | 不为 null | none | 
| conn\$1dsn | 文本 | none | 不为 null | none | 
| conn\$1apply\$1delay | 整数 | none | none | none | 
| conn\$1replication\$1sets | 文本 | none | none | none | 

conn\$1sysid  
此条目所指的节点的节点标识符。

conn\$1dsn  
与 pgactive.pgactive\$1nodes `node_dsn` 相同。

conn\$1apply\$1delay  
如果设置此参数，则将设定从远程节点应用每个事务前的等待时间（以毫秒为单位）。主要用于调试。如果为 null，则全局默认值将适用。

## 使用复制集
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.replication"></a>

复制集决定要在复制操作中包含或排除的表。默认情况下，除非您使用以下函数另行指定，否则将复制所有表：
+ `pgactive_exclude_table_replication_set()` – 从复制中排除指定的表
+ `pgactive_include_table_replication_set()` – 在复制中包含指定的表

**注意**  
在配置复制集之前，请注意以下几点：  
仅在运行 `pgactive_create_group()` 后且在运行 `pgactive_join_group()` 之前，才能配置表包含或排除。
使用 `pgactive_exclude_table_replication_set()` 后，您将无法使用 `pgactive_include_table_replication_set()`。
使用 `pgactive_include_table_replication_set()` 后，您将无法使用 `pgactive_exclude_table_replication_set()`。

系统会根据您的初始配置通过不同的方式处理新创建的表：
+ 如果排除表：在 `pgactive_join_group()` 之后创建的任何新表都将自动包含在复制中。
+ 如果包含表：在 `pgactive_join_group()` 之后创建的任何新表都将自动从复制中排除。

要查看特定表的复制集配置，请使用 `pgactive.pgactive_get_table_replication_sets()` 函数。

# pgactive 函数参考
<a name="pgactive-functions-reference"></a>

在下文中，您可以找到 pgactive 函数及其参数、返回值和实际使用说明，以助您高效使用它们：

## get\$1last\$1applied\$1xact\$1info
<a name="get-last-applied-xact-info"></a>

检索指定节点的上次应用的事务信息。

**Arguments (参数)**  
+ sysid (text) - timeline OID
+ dboid (OID)

**返回类型**  
它将记录以下内容：  
+ last\$1applied\$1xact\$1id（OID）
+ last\$1applied\$1xact\$1committs（带时区的时间戳）
+ last\$1applied\$1xact\$1at（带时区的时间戳）

**使用说明**  
使用此函数可检索指定节点的上次应用的事务信息。

## pgactive\$1apply\$1pause
<a name="pgactive-apply-pause"></a>

暂停复制应用进程。

**Arguments (参数)**  
无

**返回类型**  
布尔值

**使用说明**  
调用此函数可暂停复制应用进程。

## pgactive\$1apply\$1resume
<a name="pgactive-apply-resume"></a>

恢复复制应用进程。

**Arguments (参数)**  
无

**返回类型**  
void

**使用说明**  
调用此函数可恢复复制应用进程。

## pgactive\$1is\$1apply\$1paused
<a name="pgactive-is-apply-paused"></a>

检查复制应用当前是否已暂停。

**Arguments (参数)**  
无

**返回类型**  
布尔值

**使用说明**  
使用此函数可检查复制应用当前是否已暂停。

## pgactive\$1create\$1group
<a name="pgactive-create-group"></a>

通过将独立数据库转换为初始节点来创建 pgactive 组。



**Arguments (参数)**  
+ node\$1name (text)
+ node\$1dsn (text)
+ apply\$1delay integer DEFAULT NULL::integer - replication\$1sets text[] DEFAULT ARRAY[‘default’::text]

**返回类型**  
void

**使用说明**  
通过将独立数据库转换为初始节点来创建 pgactive 组。此函数在将节点转换为 pgactive 节点之前执行完整性检查。在使用此函数之前，请确保您的 PostgreSQL 集群有足够的可用 `max_worker_processes` 来支持 pgactive 后台工作线程。

## pgactive\$1detach\$1nodes
<a name="pgactive-detach-nodes"></a>

从 pgactive 组中移除指定节点。

**Arguments (参数)**  
+ p\$1nodes (text[])

**返回类型**  
void

**使用说明**  
使用此函数可从 pgactive 组中移除指定节点。

## pgactive\$1exclude\$1table\$1replication\$1set
<a name="pgactive-exclude-table-replication-set"></a>

将特定表排除在复制之外。

**Arguments (参数)**  
+ p\$1relation（regclass）

**返回类型**  
void

**使用说明**  
使用此函数可将特定表排除在复制之外。

## pgactive\$1get\$1replication\$1lag\$1info
<a name="pgactive-get-replication-lag-info"></a>

检索详细的复制滞后信息，包括节点详细信息、WAL 状态和 LSN 值。

**Arguments (参数)**  
无

**返回类型**  
SETOF 记录 - node\$1name text - node\$1sysid text - application\$1name text - slot\$1name text - active boolean - active\$1pid integer - pending\$1wal\$1decoding bigint - 发送者节点上待解码的 WAL 的近似大小（以字节为单位）- pending\$1wal\$1to\$1apply bigint - 要在接收节点上应用的 WAL 的近似大小（以字节为单位）- restart\$1lsn pg\$1lsn - confirmed\$1flush\$1lsn pg\$1lsn - sent\$1lsn pg\$1lsn - write\$1lsn pg\$1lsn - flush\$1lsn pg\$1lsn - replay\$1lsn pg\$1lsn

**使用说明**  
调用此函数可检索复制滞后信息，包括节点详细信息、WAL 状态和 LSN 值。

## pgactive\$1get\$1stats
<a name="pgactive-get-stats"></a>

检索 pgactive 复制统计信息。

**Arguments (参数)**  
无

**返回类型**  
SETOF 记录 - rep\$1node\$1id oid - rilocalid oid - riremoteid text - nr\$1commit bigint - nr\$1rollback bigint - nr\$1insert bigint - nr\$1insert\$1conflict bigint - nr\$1update bigint - nr\$1update\$1conflict bigint - nr\$1delete bigint - nr\$1delete\$1conflict bigint - nr\$1disconnect bigint

**使用说明**  
使用此函数可检索 pgactive 复制统计信息。

## pgactive\$1get\$1table\$1replication\$1sets
<a name="pgactive-get-table-replication-sets"></a>

获取特定关系的复制集配置。

**Arguments (参数)**  
+ relation (regclass)

**返回类型**  
SETOF 记录

**使用说明**  
调用此函数可获取特定关系的复制集配置。

## pgactive\$1include\$1table\$1replication\$1set
<a name="pgactive-include-table-replication-set"></a>

将特定表包含在复制中。

**Arguments (参数)**  
+ p\$1relation（regclass）

**返回类型**  
void

**使用说明**  
使用此函数可将特定表包含在复制中。

## pgactive\$1join\$1group
<a name="pgactive-join-group"></a>

向现有 pgactive 组添加节点。

**Arguments (参数)**  
+ node\$1name (text)
+ node\$1dsn (text)
+ join\$1using\$1dsn (text)
+ apply\$1delay (integer，可选)
+ replication\$1sets (text[]，默认值：['default'])
+ bypass\$1collation\$1check (boolean，默认值：false)
+ bypass\$1node\$1identifier\$1creation (boolean，默认值：false)
+ bypass\$1user\$1tables\$1check (boolean，默认值：false)

**返回类型**  
void

**使用说明**  
调用此函数可将节点添加到现有 pgactive 组中。确保您的 PostgreSQL 集群有足够的 max\$1worker\$1processes 以供 pgactive 后台工作线程使用。

## pgactive\$1remove
<a name="pgactive-remove"></a>

从本地节点中移除所有 pgactive 组件。

**Arguments (参数)**  
+ force (boolean，默认值：false)

**返回类型**  
void

**使用说明**  
调用此函数可从本地节点中移除所有 pgactive 组件。

## pgactive\$1snowflake\$1id\$1nextval
<a name="pgactive-snowflake-id-nextval"></a>

生成特定于节点的唯一序列值。

**Arguments (参数)**  
+ regclass

**返回类型**  
bigint

**使用说明**  
使用此函数可生成特定于节点的唯一序列值。

## pgactive\$1update\$1node\$1conninfo
<a name="pgactive-update-node-conninfo"></a>

更新 pgactive 节点的连接信息。

**Arguments (参数)**  
+ node\$1name\$1to\$1update (text)
+ node\$1dsn\$1to\$1update (text)

**返回类型**  
void

**使用说明**  
使用此函数可更新 pgactive 节点的连接信息。

## pgactive\$1wait\$1for\$1node\$1ready
<a name="pgactive-wait-for-node-ready"></a>

监控组创建或加入操作的进度。

**Arguments (参数)**  
+ timeout (integer，默认值：0)
+ progress\$1interval (integer，默认值：60)

**返回类型**  
void

**使用说明**  
调用此函数可监控组创建或加入操作的进度。

# 处理主动-主动复制中的冲突
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.handle-conflicts"></a>

`pgactive` 扩展适用于每个数据库，而不是每个集群。使用 `pgactive` 的每个数据库实例都是一个独立的实例，可以接受来自任何来源的数据更改。将更改发送到数据库实例时，PostgreSQL 会在本地提交更改，然后使用 `pgactive` 将更改异步复制到其它数据库实例。当两个 PostgreSQL 数据库实例几乎同时更新同一记录时，可能会发生冲突。

`pgactive` 扩展提供了冲突检测和自动解决机制。它跟踪在这两个数据库实例上提交事务的时间戳，并自动应用带有最新时间戳的更改。`pgactive` 扩展还会记录 `pgactive.pgactive_conflict_history` 表中发生冲突的时间。

`pgactive.pgactive_conflict_history` 会继续增加。您可能需要定义清除策略。可以通过定期删除一些记录或为此关系定义分区方案（然后分离、删除、截断感兴趣的分区）来完成此操作。要定期实施清除策略，一种选择是使用 `pg_cron` 扩展。请参阅 `pg_cron` 历史记录表示例的以下信息：[使用 PostgreSQL pg\$1cron 扩展安排维护计划](https://docs.aws.amazon.com/AmazonRDS/latest/UserGuide/PostgreSQL_pg_cron.html)。

# 处理主动-主动复制中的序列
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.handle-sequences"></a>

带有 `pgactive` 扩展的 RDS for PostgreSQL 数据库实例使用两种不同的序列机制来生成唯一值。

**全局序列**  
要使用全局序列，请使用 `CREATE SEQUENCE` 语句创建一个本地序列。使用 `pgactive.pgactive_snowflake_id_nextval(seqname)` 而非 `usingnextval(seqname)` 来获取序列的下一个唯一值。

以下示例创建全局序列：

```
app=> CREATE TABLE gstest (
      id bigint primary key,
      parrot text
    );
```

```
app=>CREATE SEQUENCE gstest_id_seq OWNED BY gstest.id;
```

```
app=> ALTER TABLE gstest \
      ALTER COLUMN id SET DEFAULT \
      pgactive.pgactive_snowflake_id_nextval('gstest_id_seq');
```

**分区序列**  
在分步或分区序列中，每个节点上都使用普通的 PostgreSQL 序列。每个序列的增量相同，从不同的偏移量开始。例如，在步骤 100 中，节点 1 生成序列为 101、201、301，依此类推，而节点 2 生成序列为 102、202、302，依此类推。即使节点长时间无法通信，该方案也能正常工作，但要求设计人员在建立模式时指定最大节点数，并且需要按节点进行配置。错误很容易导致序列重叠。

通过在节点上创建所需的序列，使用 `pgactive` 配置这种方法相对简单，如下所示：

```
CREATE TABLE some_table (generated_value bigint primary key);
```

```
app=> CREATE SEQUENCE some_seq INCREMENT 100 OWNED BY some_table.generated_value;
```

```
app=> ALTER TABLE some_table ALTER COLUMN generated_value SET DEFAULT nextval('some_seq');
```

然后，对每个节点调用 `setval` 以给出不同的偏移起始值，如下所示。

```
app=>
-- On node 1
SELECT setval('some_seq', 1);

-- On node 2
SELECT setval('some_seq', 2);
```