支援的版本:目前 (17) / 16 / 15 / 14 / 13
開發版本:devel
不支援的版本:12 / 11 / 10

29.2. 訂閱 #

訂閱是邏輯複製的下游。定義訂閱的節點稱為訂閱者。 訂閱定義與另一個資料庫的連線,以及它想要訂閱的一組發布(一個或多個)。

訂閱者資料庫的行為與任何其他 PostgreSQL 實例相同,並且可以透過定義自己的發布作為其他資料庫的發布者。

如果需要,訂閱者節點可以有多個訂閱。 可以定義單個發布者-訂閱者對之間的多個訂閱,在這種情況下,必須小心確保訂閱的發布物件不重疊。

每個訂閱將透過一個複製槽接收更改(請參閱第 26.2.6 節)。 可能需要額外的複製槽來初始資料同步預先存在的表格資料,這些複製槽將在資料同步結束時刪除。

邏輯複製訂閱可以作為同步複製的待命伺服器(請參閱第 26.2.8 節)。 預設情況下,待命伺服器名稱是訂閱名稱。 可以在訂閱的連線資訊中指定替代名稱作為 application_name

如果目前使用者是超級使用者,則 pg_dump 會傾印訂閱。 否則,會寫入警告並跳過訂閱,因為非超級使用者無法從 pg_subscription 目錄中讀取所有訂閱資訊。

使用CREATE SUBSCRIPTION新增訂閱,並且可以使用ALTER SUBSCRIPTION 命令隨時停止/恢復訂閱,並使用DROP SUBSCRIPTION刪除訂閱。

當訂閱被刪除並重新建立時,同步資訊將會遺失。 這表示必須重新同步資料。

綱要定義不會被複製,並且發布的表格必須存在於訂閱者上。 只有常規表格可以作為複製的目標。 例如,您無法複製到檢視。

表格會在發布者和訂閱者之間使用完整表格名稱進行比對。 不支援複製到訂閱者上名稱不同的表格。

表格的欄位也依名稱比對。 訂閱者表格中欄位的順序不需要與發布者的順序相符。 欄位的資料類型不需要相符,只要資料的文字表示形式可以轉換為目標類型即可。 例如,您可以從 integer 類型的欄位複製到 bigint 類型的欄位。 目標表格也可以有發布表格未提供的其他欄位。 任何此類欄位都將使用目標表格定義中指定的預設值填寫。 但是,二進位格式的邏輯複製更具限制性。 有關詳細資訊,請參閱binary選項 CREATE SUBSCRIPTION

29.2.1. 複製槽管理 #

如前所述,每個(活動)訂閱都會從遠端(發布)端的複製槽接收更改。

其他表格同步槽通常是暫時性的,會在內部建立以執行初始表格同步,並在不再需要時自動刪除。 這些表格同步槽具有產生的名稱:pg_%u_sync_%u_%llu(參數:訂閱 oid、表格 relid、系統識別碼 sysid

通常,使用CREATE SUBSCRIPTION建立訂閱時,會自動建立遠端複製槽,並在使用DROP SUBSCRIPTION刪除訂閱時自動刪除。 但是,在某些情況下,單獨操作訂閱和基礎複製槽可能很有用或必要。 以下是一些情境

  • 建立訂閱時,複製槽已存在。 在這種情況下,可以使用 create_slot = false 選項建立訂閱,以與現有槽關聯。

  • 建立訂閱時,無法連線到遠端主機或處於不清楚的狀態。 在這種情況下,可以使用 connect = false 選項建立訂閱。 然後將完全不會連線到遠端主機。 這就是 pg_dump 使用的。 然後必須在啟動訂閱之前手動建立遠端複製槽。

  • 當移除訂閱時,應該保留複製槽 (replication slot)。這在訂閱者資料庫被移至不同主機並從該處啟動時會很有用。在這種情況下,嘗試移除訂閱之前,請使用 ALTER SUBSCRIPTION 將該槽與訂閱取消關聯。

  • 當移除訂閱時,若遠端主機無法連線,在嘗試移除訂閱之前,請使用 ALTER SUBSCRIPTION 將該槽與訂閱取消關聯。如果遠端資料庫實例已不存在,則無需採取進一步措施。但是,如果遠端資料庫實例只是無法連線,則應手動移除複製槽(以及任何仍然存在的表格同步槽);否則它們將繼續保留 WAL,並可能最終導致磁碟空間被填滿。應仔細調查此類情況。

29.2.2. 範例:設定邏輯複製 #

在發布者 (publisher) 上建立一些測試表格。

test_pub=# CREATE TABLE t1(a int, b text, PRIMARY KEY(a));
CREATE TABLE
test_pub=# CREATE TABLE t2(c int, d text, PRIMARY KEY(c));
CREATE TABLE
test_pub=# CREATE TABLE t3(e int, f text, PRIMARY KEY(e));
CREATE TABLE

在訂閱者 (subscriber) 上建立相同的表格。

test_sub=# CREATE TABLE t1(a int, b text, PRIMARY KEY(a));
CREATE TABLE
test_sub=# CREATE TABLE t2(c int, d text, PRIMARY KEY(c));
CREATE TABLE
test_sub=# CREATE TABLE t3(e int, f text, PRIMARY KEY(e));
CREATE TABLE

在發布者端插入資料到這些表格中。

test_pub=# INSERT INTO t1 VALUES (1, 'one'), (2, 'two'), (3, 'three');
INSERT 0 3
test_pub=# INSERT INTO t2 VALUES (1, 'A'), (2, 'B'), (3, 'C');
INSERT 0 3
test_pub=# INSERT INTO t3 VALUES (1, 'i'), (2, 'ii'), (3, 'iii');
INSERT 0 3

為這些表格建立發布項目 (publication)。發布項目 pub2pub3a 不允許某些 publish 操作。發布項目 pub3b 具有列篩選器 (row filter)(請參閱 第 29.4 節)。

test_pub=# CREATE PUBLICATION pub1 FOR TABLE t1;
CREATE PUBLICATION
test_pub=# CREATE PUBLICATION pub2 FOR TABLE t2 WITH (publish = 'truncate');
CREATE PUBLICATION
test_pub=# CREATE PUBLICATION pub3a FOR TABLE t3 WITH (publish = 'truncate');
CREATE PUBLICATION
test_pub=# CREATE PUBLICATION pub3b FOR TABLE t3 WHERE (e > 5);
CREATE PUBLICATION

為這些發布項目建立訂閱項目 (subscription)。訂閱項目 sub3 訂閱 pub3apub3b。所有訂閱預設都會複製初始資料。

test_sub=# CREATE SUBSCRIPTION sub1
test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=sub1'
test_sub-# PUBLICATION pub1;
CREATE SUBSCRIPTION
test_sub=# CREATE SUBSCRIPTION sub2
test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=sub2'
test_sub-# PUBLICATION pub2;
CREATE SUBSCRIPTION
test_sub=# CREATE SUBSCRIPTION sub3
test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=sub3'
test_sub-# PUBLICATION pub3a, pub3b;
CREATE SUBSCRIPTION

觀察到初始表格資料被複製,無論發布項目的 publish 操作為何。

test_sub=# SELECT * FROM t1;
 a |   b
---+-------
 1 | one
 2 | two
 3 | three
(3 rows)

test_sub=# SELECT * FROM t2;
 c | d
---+---
 1 | A
 2 | B
 3 | C
(3 rows)

此外,由於初始資料複製會忽略 publish 操作,並且因為發布項目 pub3a 沒有列篩選器,這表示複製的表格 t3 包含所有列,即使這些列與發布項目 pub3b 的列篩選器不符。

test_sub=# SELECT * FROM t3;
 e |  f
---+-----
 1 | i
 2 | ii
 3 | iii
(3 rows)

在發布者端插入更多資料到這些表格中。

test_pub=# INSERT INTO t1 VALUES (4, 'four'), (5, 'five'), (6, 'six');
INSERT 0 3
test_pub=# INSERT INTO t2 VALUES (4, 'D'), (5, 'E'), (6, 'F');
INSERT 0 3
test_pub=# INSERT INTO t3 VALUES (4, 'iv'), (5, 'v'), (6, 'vi');
INSERT 0 3

現在發布者端的資料看起來像這樣:

test_pub=# SELECT * FROM t1;
 a |   b
---+-------
 1 | one
 2 | two
 3 | three
 4 | four
 5 | five
 6 | six
(6 rows)

test_pub=# SELECT * FROM t2;
 c | d
---+---
 1 | A
 2 | B
 3 | C
 4 | D
 5 | E
 6 | F
(6 rows)

test_pub=# SELECT * FROM t3;
 e |  f
---+-----
 1 | i
 2 | ii
 3 | iii
 4 | iv
 5 | v
 6 | vi
(6 rows)

觀察到在正常複製期間,會使用適當的 publish 操作。這表示發布項目 pub2pub3a 不會複製 INSERT。此外,發布項目 pub3b 只會複製符合 pub3b 的列篩選器的資料。現在訂閱者端的資料看起來像這樣:

test_sub=# SELECT * FROM t1;
 a |   b
---+-------
 1 | one
 2 | two
 3 | three
 4 | four
 5 | five
 6 | six
(6 rows)

test_sub=# SELECT * FROM t2;
 c | d
---+---
 1 | A
 2 | B
 3 | C
(3 rows)

test_sub=# SELECT * FROM t3;
 e |  f
---+-----
 1 | i
 2 | ii
 3 | iii
 6 | vi
(4 rows)

29.2.3. 範例:延遲複製槽建立 #

在某些情況下(例如 第 29.2.1 節),如果未自動建立遠端複製槽,則使用者必須在啟動訂閱之前手動建立它。以下範例顯示了建立槽和啟動訂閱的步驟。這些範例指定了標準邏輯解碼輸出外掛程式 (pgoutput),這是內建邏輯複製所使用的。

首先,為範例建立一個發布項目。

test_pub=# CREATE PUBLICATION pub1 FOR ALL TABLES;
CREATE PUBLICATION

範例 1:訂閱指定 connect = false

  • 建立訂閱。

    test_sub=# CREATE SUBSCRIPTION sub1
    test_sub-# CONNECTION 'host=localhost dbname=test_pub'
    test_sub-# PUBLICATION pub1
    test_sub-# WITH (connect=false);
    WARNING:  subscription was created, but is not connected
    HINT:  To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
    CREATE SUBSCRIPTION
    
  • 在發布者上,手動建立一個槽。由於在 CREATE SUBSCRIPTION 期間未指定名稱,因此要建立的槽的名稱與訂閱名稱相同,例如 "sub1"。

    test_pub=# SELECT * FROM pg_create_logical_replication_slot('sub1', 'pgoutput');
     slot_name |    lsn
    -----------+-----------
     sub1      | 0/19404D0
    (1 row)
    
  • 在訂閱者上,完成訂閱的啟動。在此之後,pub1 的表格將開始複製。

    test_sub=# ALTER SUBSCRIPTION sub1 ENABLE;
    ALTER SUBSCRIPTION
    test_sub=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
    ALTER SUBSCRIPTION
    

範例 2:訂閱指定 connect = false,但也指定了 slot_name 選項。

  • 建立訂閱。

    test_sub=# CREATE SUBSCRIPTION sub1
    test_sub-# CONNECTION 'host=localhost dbname=test_pub'
    test_sub-# PUBLICATION pub1
    test_sub-# WITH (connect=false, slot_name='myslot');
    WARNING:  subscription was created, but is not connected
    HINT:  To initiate replication, you must manually create the replication slot, enable the subscription, and refresh the subscription.
    CREATE SUBSCRIPTION
    
  • 在發布者上,使用在 CREATE SUBSCRIPTION 期間指定的相同名稱手動建立一個槽,例如 "myslot"。

    test_pub=# SELECT * FROM pg_create_logical_replication_slot('myslot', 'pgoutput');
     slot_name |    lsn
    -----------+-----------
     myslot    | 0/19059A0
    (1 row)
    
  • 在訂閱者上,剩餘的訂閱啟動步驟與之前相同。

    test_sub=# ALTER SUBSCRIPTION sub1 ENABLE;
    ALTER SUBSCRIPTION
    test_sub=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
    ALTER SUBSCRIPTION
    

範例 3:訂閱指定 slot_name = NONE

  • 建立訂閱。當 slot_name = NONE 時,也需要 enabled = falsecreate_slot = false

    test_sub=# CREATE SUBSCRIPTION sub1
    test_sub-# CONNECTION 'host=localhost dbname=test_pub'
    test_sub-# PUBLICATION pub1
    test_sub-# WITH (slot_name=NONE, enabled=false, create_slot=false);
    CREATE SUBSCRIPTION
    
  • 在發布者上,使用任何名稱手動建立一個槽,例如 "myslot"。

    test_pub=# SELECT * FROM pg_create_logical_replication_slot('myslot', 'pgoutput');
     slot_name |    lsn
    -----------+-----------
     myslot    | 0/1905930
    (1 row)
    
  • 在訂閱者上,將訂閱與剛剛建立的槽名稱關聯起來。

    test_sub=# ALTER SUBSCRIPTION sub1 SET (slot_name='myslot');
    ALTER SUBSCRIPTION
    
  • 剩餘的訂閱啟動步驟與之前相同。

    test_sub=# ALTER SUBSCRIPTION sub1 ENABLE;
    ALTER SUBSCRIPTION
    test_sub=# ALTER SUBSCRIPTION sub1 REFRESH PUBLICATION;
    ALTER SUBSCRIPTION
    

提交更正

如果您在文件中發現任何不正確的地方,或與您使用特定功能的經驗不符,或需要進一步說明,請使用此表單報告文件問題。