

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

# 為 Amazon RDS for PostgreSQL 執行邏輯複寫
<a name="PostgreSQL.Concepts.General.FeatureSupport.LogicalReplication"></a>

從 10.4 版開始，RDS for PostgreSQL 支援 PostgreSQL 10 中引進的發佈與訂閱 SQL 語法。如需進一步了解，請參閱 PostgreSQL 文件中的[邏輯複寫](https://www.postgresql.org/docs/current/logical-replication.html)。

**注意**  
除了在 PostgreSQL 10 中引進的原生 PostgreSQL 邏輯複寫功能之外，RDS for PostgreSQL 也支援 `pglogical` 延伸模組。如需詳細資訊，請參閱[使用 pglogical 跨執行個體同步資料](Appendix.PostgreSQL.CommonDBATasks.pglogical.md)。

在下文中，您可以了解如何設定 RDS for PostgreSQL 資料庫執行個體邏輯複寫的資訊。

**Topics**
+ [了解邏輯複寫和邏輯解碼](#PostgreSQL.Concepts.General.FeatureSupport.LogicalDecoding)
+ [使用邏輯複寫槽](#PostgreSQL.Concepts.General.FeatureSupport.LogicalReplicationSlots)
+ [使用邏輯複寫來複寫資料表層級資料](#PostgreSQL.Concepts.LogicalReplication.Tables)

## 了解邏輯複寫和邏輯解碼
<a name="PostgreSQL.Concepts.General.FeatureSupport.LogicalDecoding"></a>

RDS for PostgreSQL 支援使用 PostgreSQL 的邏輯複寫槽來串流預寫日誌 (WAL) 變更。它也支援使用邏輯解碼。您可以在執行個體上設定邏輯複寫槽，然後透過這些槽將資料庫變更串流至用戶端，例如 `pg_recvlogical`。邏輯複寫插槽在資料庫層級建立，且支援對單一資料庫的多個複寫連線。

PostgreSQL 邏輯複寫最常見的用戶端是 AWS Database Migration Service 或 Amazon EC2 執行個體上的自訂受管主機。邏輯複寫插槽沒有關於串流接收者的資訊。此外，不要求目標必須是複本資料庫。如果您設定邏輯複寫槽，但未讀取這個槽，則資料會寫入並快速填滿資料庫執行個體的儲存體。

Amazon RDS 的 PostgreSQL 邏輯複寫與邏輯解碼是以參數、複寫連線類型和安全角色來開啟。任何用戶端只要能夠在 PostgreSQL 資料庫執行個體上的資料庫建立複寫連線，都可以當作邏輯解碼的用戶端。

**開啟 RDS for PostgreSQL 資料庫執行個體的邏輯解碼**

1. 確保您正在使用的使用者帳戶具有以下角色：
   + 可讓您開啟邏輯複寫的 `rds_superuser` 角色 
   + 授權來管理邏輯槽和利用邏輯槽來串流資料的 `rds_replication` 角色

1. 將 `rds.logical_replication` 靜態參數設為 1。套用此參數時，也會設定 `wal_level`、`max_wal_senders`、`max_replication_slots` 和 `max_connections` 參數。這些參數變更會產生更多 WAL，因此請僅在使用邏輯槽時才設定 `rds.logical_replication` 參數。

1. 為了讓靜態 `rds.logical_replication` 參數生效，請重新啟動資料庫執行個體。

1. 建立邏輯複寫槽將在下一章節中說明。此程序要求您指定解碼外掛程式。目前，RDS for PostgreSQL 支援隨附於 PostgreSQL 的 test\$1decoding 和 wal2json 輸出外掛程式。

如需有關 PostgreSQL 邏輯解碼的詳細資訊，請參閱 [PostgreSQL 文件](https://www.postgresql.org/docs/current/static/logicaldecoding-explanation.html)。

## 使用邏輯複寫槽
<a name="PostgreSQL.Concepts.General.FeatureSupport.LogicalReplicationSlots"></a>

您可以利用 SQL 命令來使用邏輯槽。例如，下列命令使用預設 PostgreSQL 輸出外掛程式 `test_slot`，建立一個名為 `test_decoding` 的邏輯槽。

```
SELECT * FROM pg_create_logical_replication_slot('test_slot', 'test_decoding');
slot_name    | xlog_position
-----------------+---------------
regression_slot | 0/16B1970
(1 row)
```

若要列出邏輯槽，請使用下列命令。

```
SELECT * FROM pg_replication_slots;
```

若要捨棄邏輯槽，請使用下列命令。

```
SELECT pg_drop_replication_slot('test_slot');
pg_drop_replication_slot
-----------------------
(1 row)
```

如需有關使用邏輯複寫槽的其他範例，請參閱 PostgreSQL 文件中的[邏輯解碼範例](https://www.postgresql.org/docs/9.5/static/logicaldecoding-example.html)。

建立邏輯複寫槽之後，即可開始串流。下列範例顯示如何透過串流複寫協定來控制邏輯解碼。此範例會使用包含在 PostgreSQL 發行版中的 pg\$1recvlogical 程式。這會需要將用戶端身分驗證設為允許複寫連線。

```
pg_recvlogical -d postgres --slot test_slot -U postgres
    --host -instance-name.111122223333.aws-region.rds.amazonaws.com 
    -f -  --start
```

若要查看檢視 `pg_replication_origin_status` 的內容，請查詢 `pg_show_replication_origin_status` 函式。

```
SELECT * FROM pg_show_replication_origin_status();
local_id | external_id | remote_lsn | local_lsn
----------+-------------+------------+-----------
(0 rows)
```

## 使用邏輯複寫來複寫資料表層級資料
<a name="PostgreSQL.Concepts.LogicalReplication.Tables"></a>

您可以使用邏輯複寫，將資料從來源資料表複寫至 RDS for PostgreSQL 中的目標資料表。邏輯複寫會先從來源資料表執行現有資料的初始載入，然後繼續複寫進行中的變更。

1. 

**建立來源資料表**

   連線至 RDS for PostgreSQL 資料庫執行個體中的來源資料庫：

   ```
   source=> CREATE TABLE testtab (slno int primary key);
   CREATE TABLE
   ```

1. 

**將資料插入來源資料表中**

   ```
   source=> INSERT INTO testtab VALUES (generate_series(1,1000));
   INSERT 0 1000
   ```

1. 

**建立來源資料表的發行集**
   + 建立來源資料表的發行集：

     ```
     source=> CREATE PUBLICATION testpub FOR TABLE testtab;
     CREATE PUBLICATION
     ```
   + 使用 SELECT 查詢來驗證已建立之發行集的詳細資訊：

     ```
     source=> SELECT * FROM pg_publication;
       oid   | pubname | pubowner | puballtables | pubinsert | pubupdate | pubdelete | pubtruncate | pubviaroot
     --------+---------+----------+--------------+-----------+-----------+-----------+-------------+------------
      115069 | testpub |    16395 | f            | t         | t         | t         | t           | f
     (1 row)
     ```
   + 確認來源資料表已新增至發行集：

     ```
     source=> SELECT * FROM pg_publication_tables; 
     pubname | schemaname | tablename
     ---------+------------+-----------
      testpub | public     | testtab
     (1 rows)
     ```
   + 若要複寫資料庫中的所有資料表，請使用：

     ```
     CREATE PUBLICATION testpub FOR ALL TABLES;
     ```
   + 如果已為個別資料表建立發行集，且您需要新增資料表，您可以執行以下查詢，將任何新資料表新增至現有的發行集：

     ```
     ALTER PUBLICATION <publication_name> add table <new_table_name>;
     ```

1. 

**連線至目標資料庫並建立目標資料表**
   + 連線至目標資料庫執行個體中的目標資料庫。使用與來源資料表相同的名稱建立目標資料表：

     ```
     target=> CREATE TABLE testtab (slno int primary key);
     CREATE TABLE
     ```
   + 對目標資料表執行 SELECT 查詢，以確定目標資料表中沒有資料：

     ```
         
     target=> SELECT count(*) FROM testtab;
      count
     -------
          0
     (1 row)
     ```

1. 

**在目標資料庫中建立和驗證訂閱**
   + 在目標資料庫中建立訂閱：

     ```
     target=> CREATE SUBSCRIPTION testsub 
     CONNECTION 'host=<source RDS/host endpoint> port=5432 dbname=<source_db_name> user=<user> password=<password>' 
     PUBLICATION testpub;
     NOTICE:  Created replication slot "testsub" on publisher
     CREATE SUBSCRIPTION
     ```
   + 使用 SELECT 查詢來驗證訂閱已啟用：

     ```
     target=> SELECT oid, subname, subenabled, subslotname, subpublications FROM pg_subscription;
       oid  | subname | subenabled | subslotname | subpublications
     -------+---------+------------+-------------+-----------------
      16434 | testsub | t          | testsub     | {testpub}
     (1 row)
     ```
   + 訂閱建立後，會將來源資料表中的所有資料載入至目標資料表。對目標資料表執行 SELECT 查詢，以確認初始資料載入：

     ```
     target=> SELECT count(*) FROM testtab;
      count
     -------
       1000
     (1 row)
     ```

1. 

**驗證來源資料庫中的複寫插槽**

   在目標資料庫中建立訂閱，將會在來源資料庫中建立複寫插槽。對來源資料庫執行下列 SELECT 查詢，以驗證複寫插槽詳細資訊：

   ```
   source=> SELECT * FROM pg_replication_slots;
    
   slot_name |  plugin  | slot_type | datoid | database | temporary | active | active_pid | xmin | catalog_xmin | restart_lsn | confirmed_flush_lsn | wal_status | safe_wal_size
   ----------+----------+-----------+--------+----------+-----------+--------+------------+------+--------------+-------------+---------------------+------------+---------------
   testsub   | pgoutput | logical   | 115048 | source   | f         | t      |        846 |      |         6945 | 58/B4000568 | 58/B40005A0         | reserved   |
   (1 row)
   ```

1. 

**測試複寫**
   + 將資料列插入來源資料表中，藉以測試來源資料表中的資料變更是否複寫至目標資料表：

     ```
     source=> INSERT INTO testtab VALUES(generate_series(1001,2000));
     INSERT 0 1000
     
     source=> SELECT count(*) FROM testtab; 
      count
     -------
       2000
     (1 row)
     ```
   + 驗證目標資料表中的資料列數，以確認正在複寫新的插入項目：

     ```
     target=> SELECT count(*) FROM testtab;
      count
     -------
       2000
     (1 row)
     ```

1. 

**在新增資料表後重新整理訂閱**
   + 當您將新資料表新增至現有發行集時，必須重新整理訂閱，變更才會生效：

     ```
     ALTER SUBSCRIPTION <subscription_name> REFRESH PUBLICATION;
     ```
   + 此命令會從發佈者擷取缺少的資料表資訊，並開始複寫自訂閱建立或上次重新整理以來新增至訂閱發行集的資料表。