

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 使用 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 管理主控台 ，並在 [https://console.aws.amazon.com/rds/](https://console.aws.amazon.com/rds/)：// 開啟 Amazon 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 延伸模組功能**

若要`pgactive`使用 初始化 AWS CLI，請呼叫 [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>

下列程序說明如何在兩個 RDS for PostgreSQL 資料庫執行個體 (其中 `pgactive` 可用) 之間啟動主動-主動複寫。若要執行多區域高可用性範例，您需要在兩個不同的區域部署 Amazon RDS for PostgreSQL 執行個體，並設定 VPC 對等互連。如需詳細資訊，請參閱 [VPC 對等互連](https://docs.aws.amazon.com/vpc/latest/peering/what-is-vpc-peering.html)。

**注意**  
在多個地區之間傳送流量可能會產生額外費用。

這些步驟假設 RDS for PostgreSQL 資料庫執行個體已使用 `pgactive` 延伸模組啟用。如需詳細資訊，請參閱[初始化 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` 延伸模組所需的其他步驟。

這些步驟假設已有另一個 RDS for PostgreSQL 資料庫執行個體使用 `pgactive` 延伸模組設定完成。如需詳細資訊，請參閱[初始化 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  
設定作用中為 false 時的警示，表示插槽目前未使用 (訂閱者執行個體已與發佈者中斷連線)。

pending\$1wal\$1decoding  
在 PostgreSQL 的邏輯複寫中，WAL 檔案會以二進位格式儲存。發佈者必須解碼這些 WAL 變更，並將其轉換為邏輯變更 (例如插入、更新或刪除操作)。  
指標 pending\$1wal\$1decoding 會顯示在發佈者端等待解碼的 WAL 檔案數目。  
此數字可能會因為下列因素而增加：  
+ 當訂閱者未連線時，作用中狀態將為 false，而 pending\$1wal\$1decoding 將會增加
+ 插槽處於作用中狀態，但發佈者無法跟上 WAL 變更的數量

pending\$1wal\$1to\$1apply  
指標 pending\$1wal\$1apply 表示在訂閱者端等待套用的 WAL 檔案數目。  
有幾個因素可能會阻止訂閱者套用變更，並可能導致磁碟已滿的情況：  
+ 結構描述差異 - 例如，當您對名為範例的資料表進行 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` 延伸模組主要參數的參考：


| 參數 | 單位 | 預設 | Description | 
| --- | --- | --- | --- | 
| 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 套用工作者的遞交行為。當停用 (關閉) 時，套用工作者會執行非同步遞交，這可在套用操作期間改善 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 鎖定操作的預設偵錯日誌層級。設定時，此設定會導致 DDL 鎖定相關的訊息在 LOG 偵錯層級發出，而不是其預設層級。使用此參數來監控 DDL 鎖定活動，而無需在整個伺服器上啟用詳細 `DEBUG1` 或 `DEBUG2` 日誌層級。 可用日誌層級，依詳細程度增加： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.pgactive.parameters.html) 如需監控選項的詳細資訊，請參閱「監控全域 DDL 鎖定」。  當您重新載入組態時，此設定的變更會生效。您不需要重新啟動伺服器。   | 

## 其他 pgactive 延伸模組參數
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.addparams"></a>

下表顯示 `pgactive` 延伸模組可用的較不常用和內部組態選項。


| 參數 | 單位 | 預設 | Description | 
| --- | --- | --- | --- | 
| 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` 時，節點會像非 pgcctive 節點一樣處理 DDL 變更。使用此參數時適用以下要求： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/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_tw/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_tw/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_tw/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.pgactive.parameters.html)  啟用此記錄設定可能會影響效能。建議您僅在需要進行疑難排解時啟用它。當您重新載入組態時，此設定的變更會生效。您不需要重新啟動伺服器。   | 
| pgactive.extra\$1apply\$1connection\$1options |  | – | 您可以為具有 pgactive 節點的所有對等節點連線設定連線參數。這些參數會控制保持連線和 SSL 模式等設定。根據預設，pgactive 會使用下列連線參數： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/AmazonRDS/latest/UserGuide/Appendix.PostgreSQL.CommonDBATasks.pgactive.parameters.html) 若要覆寫預設參數，請使用下列類似命令： pgactive.extra\$1apply\$1connection\$1options = 'keepalives=0' 個別節點連線字串優先於這些設定和 pgactive 的內建連線選項。如需連線字串格式的詳細資訊，請參閱 [libpq 連線字串](https://www.postgresql.org/docs/current/libpq-connect.html#LIBPQ-CONNSTRING)。 建議您保持預設保持連線設定為啟用狀態。只有在大型交易透過不可靠的網路完成時遇到問題的情況下，才停用保持連線。  建議您保持預設保持連線設定為啟用狀態。只有在大型交易透過不可靠的網路完成時遇到問題的情況下，才停用保持連線。當您重新載入組態時，此設定的變更會生效。您不需要重新啟動伺服器。  | 
| pgactive.init\$1node\$1parallel\$1jobs (int) |  | – | 指定 `pg_dump` 和 `pg_restore` 可在與 `pgactive.pgactive_join_group` 函數的邏輯節點聯結期間使用的平行任務數目。 當您重新載入組態時，此設定的變更會生效。您不需要重新啟動伺服器。 | 
| pgactive.max\$1nodes | `int` | 4 |  指定 pgactive 延伸模組群組中允許的節點數目上限。預設值為 4 個節點。設定此參數的值時，您必須考量下列事項： [\[See the AWS documentation website for more details\]](http://docs.aws.amazon.com/zh_tw/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 索引
+ 序列
+ 資料列和關聯鎖定
+ 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 透過 last-update-wins 處理或您的自訂衝突處理常式來解決偵測到的衝突。

資料列衝突包括：
+ 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 會使用 last-update-wins 邏輯或您的自訂衝突處理常式來解決這些衝突。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 無法針對這些操作使用 last-update-wins 邏輯自動解決衝突。您必須確保 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 無法套用 last-update-wins 衝突解決方法。此行為類似於違反多個限制條件的 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 無法判斷 UPDATE 是用於新資料列 (尚未收到 INSERT) 或刪除的資料列。在這兩種情況下，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>

 pgactive 連結不支援排除限制條件，並限制其建立。

**注意**  
如果您將現有的獨立資料庫轉換為 pgactivelink 資料庫，請手動捨棄所有排除限制條件。

在分散式非同步系統中，無法保證沒有任何一組資料列違反限制條件。這是因為不同節點上的所有交易都完全隔離。排除限制條件可能會導致重新執行死鎖，其中由於違反排除限制條件，重新執行無法從任何節點進展到另一個節點。

如果您強制 pgactive 連結建立排除限制條件，或在將獨立資料庫轉換為 pgactive 連結時未捨棄現有限制條件，複寫可能會中斷。若要還原複寫進度，請移除或更改與傳入遠端元組衝突的本機元組，以便套用遠端交易。

### 全域資料衝突
<a name="Appendix.PostgreSQL.CommonDBATasks.pgactive.actact.conflict12"></a>

使用 pgactivelink 時，如果節點具有不同的全域 PostgreSQL 全系統資料 (例如角色)，可能會發生衝突。這些衝突可能會導致操作 (主要是 DDL) 在一個節點上成功並遞交，但無法套用至其他節點。

如果使用者存在於一個節點，但不存在於另一個節點，則可能會發生複寫問題：
+ Node1 有名為 `fred` 的使用者，但 Node2 上不存在此使用者
+ 當 `fred` 在 Node1 上建立資料表時，資料表會以具備擁有者身分的 `fred` 進行複寫
+ 當此 DDL 命令套用至 Node2 時會失敗，因為使用者 `fred` 不存在
+ 此失敗會在 Node2 上的 PostgreSQL 日誌中產生錯誤，並讓 `pgactive.pgactive_stats.nr_rollbacks` 計數器增量

**解決方案：**在 Node2 上建立使用者 `fred`。使用者不需要相同的許可，但必須同時存在於兩個節點上。

如果資料表存在於一個節點，但不存在於另一個節點，資料修改操作將會失敗：
+ Node1 具有名為 `foo` 的資料表，該資料表不存在於 Node2
+ 複寫至 Node2 時，Node1 上 `foo` 資料表上的任何 DML 操作都會失敗

**解決方案：**在 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 時，才會將衝突記錄到此資料表。當 log\$1min\$1messages 設定為 `LOG` 或 `lower` 時，無論 `pgactive.log_conflicts_to_table` 設定為何，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 會儲存參與主動-主動複寫群組之節點的相關資訊。


| 資料行 | Type | 定序 | Nullable | 預設 | 
| --- | --- | --- | --- | --- | 
| node\$1sysid | text | – | 非 NULL | – | 
| node\$1timeline | oid | – | 非 NULL | – | 
| node\$1dboid | oid | – | 非 NULL | – | 
| node\$1status | char | – | 非 NULL | – | 
| node\$1name | text | – | 非 NULL | – | 
| node\$1dsn | text | – | 非 NULL | – | 
| node\$1init\$1from\$1dsn | text | – | 非 NULL | – | 
| node\$1read\$1only | boolean | – | – | false | 
| node\$1seq\$1id | smallint | – | 非 NULL | – | 

**node\$1sysid**  
節點的唯一 ID，在 `pgactive_create_group` 或 `pgactive_join_group` 期間產生

**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 會儲存每個節點的連線詳細資訊。


| 資料行 | Type | 定序 | Nullable | 預設 | 
| --- | --- | --- | --- | --- | 
| conn\$1sysid | text | 無 | 非 NULL | 無 | 
| conn\$1timeline | oid | 無 | 非 NULL | 無 | 
| conn\$1dboid | oid | 無 | 非 NULL | 無 | 
| conn\$1dsn | text | 無 | 非 NULL | 無 | 
| conn\$1apply\$1delay | integer | 無 | 無 | 無 | 
| conn\$1replication\$1sets | text | 無 | 無 | 無 | 

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 (文字) - 時間軸 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 (引數)**  
無

**傳回類型**  
boolean

**使用須知**  
呼叫此函數可暫停複寫套用程序。

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

繼續複寫套用程序。

**Arguments (引數)**  
無

**傳回類型**  
void

**使用須知**  
呼叫此函數可繼續複寫套用程序。

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

檢查複寫目前是否暫停。

**Arguments (引數)**  
無

**傳回類型**  
boolean

**使用須知**  
使用此函數可檢查複寫套用目前是否暫停。

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

藉由將獨立資料庫轉換為初始節點，來建立 pgactive 群組。



**Arguments (引數)**  
+ node\$1name (文字)
+ node\$1dsn (文字)
+ 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 (文字)
+ node\$1dsn (文字)
+ join\$1using\$1dsn (文字)
+ apply\$1delay (整數，選用)
+ replication\$1sets (text[]，預設值：['default'])
+ bypass\$1collation\$1check (布林值，預設值：false)
+ bypass\$1node\$1identifier\$1creation (布林值，預設值：false)
+ bypass\$1user\$1tables\$1check (布林值，預設值：false)

**傳回類型**  
void

**使用須知**  
呼叫此函數，可將節點新增至現有的 pgactive 群組。請確定您的 PostgreSQL 叢集有足夠的 max\$1worker\$1processes 供 pgactive 背景工作者使用。

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

從本機節點中移除所有 pgactive 元件。

**Arguments (引數)**  
+ force (布林值，預設值：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 (文字)
+ node\$1dsn\$1to\$1update (文字)

**傳回類型**  
void

**使用須知**  
使用此函數可更新 pgactive 節點的連線資訊。

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

監控群組建立或加入操作的進度。

**Arguments (引數)**  
+ 逾時 (整數，預設值：0)
+ progress\$1interval (整數，預設值：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` 陳述式建立本機序列。不要使用 `usingnextval(seqname)`，而是使用 `pgactive.pgactive_snowflake_id_nextval(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');
```

然後在每個節點上呼叫，以提供不同的偏移量起始值，如下所示。

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

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