訂閱是邏輯複製的下游。定義訂閱的節點稱為訂閱者。 訂閱定義與另一個資料庫的連線,以及它想要訂閱的一組發布(一個或多個)。
訂閱者資料庫的行為與任何其他 PostgreSQL 實例相同,並且可以透過定義自己的發布作為其他資料庫的發布者。
如果需要,訂閱者節點可以有多個訂閱。 可以定義單個發布者-訂閱者對之間的多個訂閱,在這種情況下,必須小心確保訂閱的發布物件不重疊。
每個訂閱將透過一個複製槽接收更改(請參閱第 26.2.6 節)。 可能需要額外的複製槽來初始資料同步預先存在的表格資料,這些複製槽將在資料同步結束時刪除。
邏輯複製訂閱可以作為同步複製的待命伺服器(請參閱第 26.2.8 節)。 預設情況下,待命伺服器名稱是訂閱名稱。 可以在訂閱的連線資訊中指定替代名稱作為 application_name
。
如果目前使用者是超級使用者,則 pg_dump
會傾印訂閱。 否則,會寫入警告並跳過訂閱,因為非超級使用者無法從 pg_subscription
目錄中讀取所有訂閱資訊。
使用CREATE SUBSCRIPTION
新增訂閱,並且可以使用ALTER SUBSCRIPTION
命令隨時停止/恢復訂閱,並使用DROP SUBSCRIPTION
刪除訂閱。
當訂閱被刪除並重新建立時,同步資訊將會遺失。 這表示必須重新同步資料。
綱要定義不會被複製,並且發布的表格必須存在於訂閱者上。 只有常規表格可以作為複製的目標。 例如,您無法複製到檢視。
表格會在發布者和訂閱者之間使用完整表格名稱進行比對。 不支援複製到訂閱者上名稱不同的表格。
表格的欄位也依名稱比對。 訂閱者表格中欄位的順序不需要與發布者的順序相符。 欄位的資料類型不需要相符,只要資料的文字表示形式可以轉換為目標類型即可。 例如,您可以從 integer
類型的欄位複製到 bigint
類型的欄位。 目標表格也可以有發布表格未提供的其他欄位。 任何此類欄位都將使用目標表格定義中指定的預設值填寫。 但是,二進位格式的邏輯複製更具限制性。 有關詳細資訊,請參閱binary
選項 CREATE SUBSCRIPTION
。
如前所述,每個(活動)訂閱都會從遠端(發布)端的複製槽接收更改。
其他表格同步槽通常是暫時性的,會在內部建立以執行初始表格同步,並在不再需要時自動刪除。 這些表格同步槽具有產生的名稱:“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,並可能最終導致磁碟空間被填滿。應仔細調查此類情況。
在發布者 (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)。發布項目 pub2
和 pub3a
不允許某些 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
訂閱 pub3a
和 pub3b
。所有訂閱預設都會複製初始資料。
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
操作。這表示發布項目 pub2
和 pub3a
不會複製 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.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 = false
和 create_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
如果您在文件中發現任何不正確的地方,或與您使用特定功能的經驗不符,或需要進一步說明,請使用此表單報告文件問題。