使用 pgactive 支持主动-主动复制 - Amazon Relational Database Service

使用 pgactive 支持主动-主动复制

pgactive 扩展使用主动-主动复制来支持和协调多个 RDS for PostgreSQL 数据库上的写入操作。Amazon RDS for PostgreSQL 支持以下版本的 pgactive 扩展。

  • 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

注意

当复制配置中的多个数据库上有写入操作时,可能会发生冲突。有关更多信息,请参阅 处理主动-主动复制中的冲突

初始化 pgactive 扩展功能

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

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

您可以使用 AWS Management Console或 AWS CLI 创建所需的 RDS for PostgreSQL 数据库实例。以下步骤假设您的 RDS for PostgreSQL 数据库实例与自定义数据库参数组相关联。有关创建自定义数据库参数组的信息,请参阅Amazon RDS 的参数组

初始化 pgactive 扩展功能
  1. 登录 AWS Management Console 并通过以下网址打开 Amazon RDS 控制台:https://console.aws.amazon.com/rds/

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

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

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

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

  6. 选择 Save changes(保存更改)

  7. 在 Amazon RDS 控制台的导航窗格中,选择数据库

  8. 选择您的 RDS for PostgreSQL 数据库实例,然后从操作菜单中选择重启

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

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

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

    psql --host=mydb.111122223333.aws-region.rds.amazonaws.com --port=5432 --username=master username --password --dbname=postgres
  11. 要验证 pgactive 是否初始化,可以运行以下命令。

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

    如果 pgactiveshared_preload_libraries 中,则前面的命令将返回以下内容:

    ?column? ---------- t
  12. 创建扩展,如下所示。

    postgres=> CREATE EXTENSION pgactive;
初始化 pgactive 扩展功能

要使用 AWS CLI 设置 pgactive,请调用 modify-db-parameter-group 操作来修改自定义参数组中的某些参数,如以下过程所示。

  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
  2. 使用以下 AWS CLI 命令重启 RDS for PostgreSQL 数据库实例,以便初始化 pgactive 库。

    aws rds reboot-db-instance \ --db-instance-identifier your-instance \ --region aws-region
  3. 当实例可用时,使用 psql 连接到 RDS for PostgreSQL 数据库实例

    psql --host=mydb.111122223333.aws-region.rds.amazonaws.com --port=5432 --username=master user --password --dbname=postgres
  4. 创建扩展,如下所示。

    postgres=> CREATE EXTENSION pgactive;

RDS for PostgreSQL 数据库实例设置主动-主动复制

以下过程说明如何在同一区域中运行 PostgreSQL 15.4 或更高版本的两个 RDS for PostgreSQL 数据库实例之间启动主动-主动复制。要运行多区域高可用性示例,您需要在两个不同的区域中部署 Amazon RDS for PostgreSQL 实例,并设置 VPC 对等。有关更多信息,请参阅 VPC 对等

注意

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

这些步骤假设已使用 pgactive 扩展设置了 RDS for PostgreSQL 数据库实例。有关更多信息,请参阅 初始化 pgactive 扩展功能

使用 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=master username --password --dbname=postgres
  2. 使用以下命令在 RDS for PostgreSQL 实例上创建数据库:

    postgres=> CREATE DATABASE app;
  3. 使用以下命令将连接切换到新数据库:

    \c app
  4. 要检查 shared_preload_libraries 参数是否包含 pgactive,请运行以下命令:

    app=>SELECT setting ~ 'pgactive' FROM pg_catalog.pg_settings WHERE name = 'shared_preload_libraries';
    ?column? ---------- t
  5. 使用以下 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);
    2. 使用以下 SQL 语句用一些示例数据填充表。

      app=> INSERT INTO inventory.products (id, product_name) VALUES (1, 'soap'), (2, 'shampoo'), (3, 'conditioner');
    3. 使用以下 SQL 语句验证表中是否存在数据。

      app=>SELECT count(*) FROM inventory.products; count ------- 3
  6. 在现有数据库上创建 pgactive 扩展。

    app=> CREATE EXTENSION pgactive;
  7. 使用以下命令创建并初始化 pgactive 组:

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

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

    注意

    要在可公开访问的数据库实例上成功执行此步骤,必须通过将 rds.custom_dns_resolution 参数设置为 1 将其开启。

  8. 要检查数据库实例是否就绪,请使用以下命令:

    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 扩展功能

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

    psql --host=secondinstance.111122223333.aws-region.rds.amazonaws.com --port=5432 --username=master username --password --dbname=postgres
  2. 使用以下命令在第二个 RDS for PostgreSQL 数据库实例上创建数据库:

    postgres=> CREATE DATABASE app;
  3. 使用以下命令将连接切换到新数据库:

    \c app
  4. 在现有数据库上创建 pgactive 扩展。

    app=> CREATE EXTENSION pgactive;
  5. 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=master username password=PASSWORD', join_using_dsn := 'dbname=app host=firstinstance.111122223333.aws-region.rds.amazonaws.com user=postgres password=PASSWORD');

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

  6. 要检查数据库实例是否就绪,请使用以下命令:

    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 将在两个数据库实例之间同步数据。

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

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

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

    count ------- 3
  8. 运行以下命令插入新值:

    app=> INSERT INTO inventory.products (id, product_name) VALUES ('lotion');
  9. 连接到第一个数据库实例的数据库并运行以下查询:

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

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

    count ------- 4
pgactive 组中分离和移除数据库实例

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

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

    app=> SELECT * FROM pgactive.pgactive_detach_nodes(ARRAY[‘node2-app']);
  2. 使用以下命令从第二个数据库实例中移除 pgactive 扩展:

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

    要强制移除此扩展,请执行以下操作:

    app=> SELECT * FROM pgactive.pgactive_remove(true);
  3. 使用以下命令删除扩展:

    app=> DROP EXTENSION pgactive;

处理主动-主动复制中的冲突

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

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

pgactive.pgactive_conflict_history 会继续增加。您可能需要定义清除策略。可以通过定期删除一些记录或为此关系定义分区方案(然后分离、删除、截断感兴趣的分区)来完成此操作。要定期实施清除策略,一种选择是使用 pg_cron 扩展。请参阅 pg_cron 历史记录表示例的以下信息:使用 PostgreSQL pg_cron 扩展安排维护计划

处理主动-主动复制中的序列

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

全局序列

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

以下示例创建全局序列:

postgres=> CREATE TABLE gstest ( id bigint primary key, parrot text );
postgres=>CREATE SEQUENCE gstest_id_seq OWNED BY gstest.id;
postgres=> 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);
postgres=> CREATE SEQUENCE some_seq INCREMENT 100 OWNED BY some_table.generated_value;
postgres=> ALTER TABLE some_table ALTER COLUMN generated_value SET DEFAULT nextval('some_seq');

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

postgres=> -- On node 1 SELECT setval('some_seq', 1); -- On node 2 SELECT setval('some_seq', 2);

pgactive 扩展的参数参考

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

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

衡量 pgactive 成员之间的复制延迟

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

postgres=# SELECT *, (last_applied_xact_at - last_applied_xact_committs) AS lag FROM pgactive.pgactive_node_slots; -{ RECORD 1 ]----------------+----------------------------------------------------------------- node_name | node2-app slot_name | pgactive_5_7332551165694385385_0_5__ slot_restart_lsn | 0/1A898A8 slot_confirmed_lsn | 0/1A898E0 walsender_active | t walsender_pid | 69022 sent_lsn | 0/1A898E0 write_lsn | 0/1A898E0 flush_lsn | 0/1A898E0 replay_lsn | 0/1A898E0 last_sent_xact_id | 746 last_sent_xact_committs | 2024-02-06 18:04:22.430376+00 last_sent_xact_at | 2024-02-06 18:04:22.431359+00 last_applied_xact_id | 746 last_applied_xact_committs | 2024-02-06 18:04:22.430376+00 last_applied_xact_at | 2024-02-06 18:04:52.452465+00 lag | 00:00:30.022089

pgactive 扩展的局限性

  • 所有表都需要主键,否则不允许使用更新和删除。不应更新“主键”列中的值。

  • 序列可能存在间隙,有时可能不遵循顺序。不会复制序列。有关更多信息,请参阅 处理主动-主动复制中的序列

  • 不会复制 DDL 和大型对象。

  • 辅助唯一索引可能会导致数据差异。

  • 组中所有节点上的排序规则需要相同。

  • 跨节点负载均衡是一种反模式。

  • 大型事务可能会导致复制滞后。