Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
可以在任何實體複寫主機上定義 PUBLICATION (發佈)。 定義 PUBLICATION 的節點稱為 Publisher (發佈者)。PUBLICATION 是從一個資料表或一組資料表中所産生的一組變更,也可能被描述為變更集合或複寫集合。每個發佈僅能存在於一個資料庫中。
發佈與綱要(Schema)不同,它並不會影響資料表的存取方式。如果需要,每個資料表可以加到多個發佈之中。 發佈目前可能只包含資料表。物件必須明確加入,除非指定選項為 ALL TABLES 建立發佈。
發佈可以選擇將其產生的變更限制為 INSERT、UPDATE 、 DELETE 及 TRUNCATE 或其任意組合,類似於特定事件類型觸發器的方式。預設情況下,所有操作類型都會被複寫。這些 PUBLICATION 規範僅適用於 DML 操作;它們不會影響已同步的資料副本。 (Row filter 不會對 TRUNCATE 產生影響。請參閱第 31.3 節)。
發佈的資料表必須配置「REPLICA IDENTITY (副本識別)」,以便能夠複寫 UPDATE 和 DELETE 操作,使其在訂閱端可以識別更新或刪除適當的資料列。預設情況下,如果有主鍵的話,就以主鍵識別。另一種情況是使用唯一索引(具有某些附加要求),也可以設定為副本識別。如果該資料表沒有任何合適的方式,則可以將副本識別設定為「full」,這意味著整個資料列都作為識別。但是,這樣做的效能是非常低的,只有在沒有其他解決方案可行的情況下才可以這樣使用。如果在發佈方設定了除「full」之外的副本識別,則還必須在訂閱戶設定包含相同或更少欄位的副本標別。有關如何設定副本識別的詳細訊息,請參閱 REPLICA IDENTITY。如果沒有副本識別的資料表被加到複寫 UPDATE 或 DELETE 操作的發佈中,則隨後的 UPDATE 或 DELETE 操作將導致發佈者出錯。不論副本識別如何設定,INSERT 操作都可以繼續進行。
每個發佈可以有多個訂閱者。
使用 CREATE PUBLICATION 指令建立發佈,稍後可以使用相應的命令變更或移除發佈。
可以使用 ALTER PUBLICATION 動態加入和移除單個資料表。ADD TABLE 和 DROP TABLE 操作都是交易安全的;所以一旦交易事務提交後,資料表就會啟動或停止複寫,並且維持正確的交易快照。
邏輯複寫(Logical Replication)是一種依據複寫指標(通常是主鍵)複製資料物件及其更新的方法。我們使用術語邏輯與物理複寫相對比,物理複寫使用確切的區塊位址進行每一個字元組的複寫。PostgreSQL 同時支持這兩種機制,請參閱第 27 章。邏輯複寫允許對資料複寫和安全性進行更精細的控制。
邏輯複寫使用 publish(發佈)和 subscribe(訂閱)模式,其中一個或多個訂閱者 subscribe 發布者節點上的一個或多個 publish。 訂閱戶從他們訂閱的 publish 中提取資料,並可能隨後重新發布資料以允許串聯複寫或更複雜的複寫架構。
資料表的邏輯複寫通常始於對發佈者資料庫上的資料進行快照並將其複寫到訂閱伺服器。一旦完成,發佈者的變化就會即時發送給訂閱者。訂閱者按照與發佈者相同的順序變動資料,以確保單個訂閱內的發佈的交易事務一致性。這種資料複寫方法有時被稱為交易事務複寫。
典型的邏輯複寫情況有:
在訂閱者時常向單個資料庫或資料庫的子集發送增量變更時。
當訂閱者收到個別的變更時能觸發事件。
將多個資料庫合併成一個資料庫(例如為了分析的需求)。
在不同的 PostgreSQL 版本之間複寫。
在不同平台(例如 Linux 到 Windows)上的 PostgreSQL 伺服器之間的複寫
將複寫的資料給予不同的使用者群組存取權限。
在多個資料庫之間共享資料庫的一部份。
訂閱戶資料庫的行為與任何其他 PostgreSQL 的行為相同,並且可以透過定義其自己的發佈來用作其他資料庫的發佈者。當訂閱者被應用程序視為唯讀時,單個訂閱就不會發生衝突。另一方面,如果應用程序或其他使用者對同一組資料表執行了其他的寫入操作,則可能會出現衝突。
邏輯複寫目前有以下限制或功能不足的地方。這些可能會在將來的版本中解決。
資料庫結構和 DDL 指令不會被複寫。初始模式可以使用 pg_dump --schema-only 手動複寫。後續的結構變更需要手動保持同步。(但是,請注意,兩側的結構沒有必要完全相同。)當主要資料庫中的結構定義變更時,邏輯複寫是沒問題的:當發佈者上的結構産生變更並且複寫的資料開始到達訂閱戶但不符合資料表結構,複寫將産生錯誤,直到結構更新。在許多情況下,可以透過先將預定的架構變更套用於訂閱戶來避免間歇性的錯誤。
序列資料不會被複寫。序列支援的序列或識別欄位中的資料當然會作為資料表的一部分進行複寫,但序列本身仍然會顯示訂閱戶的初始值。如果使用者用作唯讀資料庫,那麼這通常不成問題。但是,如果打算對訂閱戶資料庫進行某種切換或故障轉移,則需要將序列更新為最新值,方法是複寫發佈者的目前資料(可能使用 pg_dump)或設定對該資料表而言足夠高的值。
支援複寫 TRUNCATE 指令,但是在 TRUNCATE 由外部鍵連結的資料表群組時必須格外小心。當複寫 TRUNCATE 操作時,訂閱者將 TRUNCATE 在發佈者上被 TRUNCATE 的同一組資料表,這些資料表群組是明確指定的或透過 CASCADE 所收集的,再減去了不屬於發佈的資料表。如果所有受影響的資料表都屬於同一個發佈,則此方法將會正常運作。但是,如果某些要在訂閱者上被 TRUNCATE 的資料表具有指向不屬於同一(或任何)訂閱的資料表的外部鍵連結,則在訂閱伺服器上套用 TRUNCATE 操作將會失敗。
Large objects(請參閱第 34 章)不會被複寫。除了將資料儲存在普通資料表中之外,沒有其他解決方法。
僅 TABLE(包括 PARTITION TABLE)支援複寫。 嘗試製寫其他類型的關連物件,例如檢視表 VIEW,具體化檢視圖 MATERIALIZED VIEW 或外部資料表 FOREIGN TABLE,將會導致錯誤。
在分割資料表之間複寫時,預設情況下,會實際複寫來自發佈者上的子資料表,因此發佈者上的分割區也必須作為有效目標資料表存在於訂閱者上。 (它們本身可以是子資料表,也可以進一步再分割,甚至可以是獨立的資料表。)PUBLICATION 還可以指定要使用分割父資料表的 identity 和 schema 來複寫變更,取代實際要複寫的各個子資料表(請參閱 CREATE PUBLICATION)。
邏輯複寫需要設定幾個系統組態選項。
在發佈端,必須將 wal_level 設定為 logical,並且必須將 max_replication_slots 設定為至少預計要連線的訂閱數量,並為資料表同步保留一些預留數量。然後 max_wal_senders 應至少設定為與 max_replication_slots 相同的數量再加上同時連線的實體副本數量。
用戶還需要設定 max_replication_slots。在這種情況下,它應至少設定為將加到訂閱伺服器的訂閱數。max_logical_replication_workers 必須至少設定為訂閱數量,再加上資料表同步的一些保留數。此外,可能需要調整 max_worker_processes 以滿足複寫程序,至少為(max_logical_replication_workers + 1)。請注意,某些延伸功能和平行查詢也需要 max_worker_processes 中的程序插槽。
由於邏輯複寫基於與物理性的串流複寫相似的體系結構,因此發佈節點上的監控與監控物理式串流複寫主節點類似(請參閱第 27.2.5.2 節)。
有關訂閱的監控訊息可以在 pg_stat_subscription 中找到。該檢視表對每個訂閱程序都有一個資料列。根據訂閱的狀態,訂閱可以有零個或多個活動訂閱程序。
一般而言,每一個啟用的訂閱會有單個 apply 程序運行。此檢視表中禁用的訂閱或當掉的訂閱將不會看到資料。如果任何資料表的初始資料同步正在進行,則會有額外的程序表示正在同步資料表。
訂閱是邏輯複寫的下游。訂閱的節點被稱為訂閱者。訂閱定義了與另一個資料庫以及它想要訂閱的一組或多組發佈(一個或多個)的連線。
訂閱資料庫的行為與任何其他 PostgreSQL 服務的行為相同,也可以透過定義自己的發佈來成為其他資料庫的發佈者。
如果需要,用戶節點可能有多個訂閱。在單個(發佈者 - 訂閱者)配對之間定義多個訂閱允許的,在這種情況下必須小心以確保訂閱的發佈對象不重疊。
每個訂閱都將透過一個複寫插槽(replication slot)接收變更(請參閱第 27.2.6 節)。額外的臨時複寫插槽可能需要用於預先存在的資料表資料才能開始初始化資料同步。
邏輯複寫訂閱可以是同步複寫的備用資料庫(請參閱第 27.2.8 節)。備用名稱預設為訂閱名稱。可以在訂閱的連線訊息中將另一個名稱以 application_name 指定。
如果目前使用者是超級使用者,則 pg_dump 會匯出訂閱的內容。否則會提示警告訊息並跳過訂閱內容,因為非超級使用者無法讀取 pg_subscription 目錄中的訂閱訊息。
訂閱是使用 CREATE SUBSCRIPTION 加入的,可以隨時使用 ALTER SUBSCRIPTION 指令停止或恢復,並使用 DROP SUBSCRIPTION 移除訂閱。
當訂閱被移除並重新建立時,將會失去同步訊息。這意味著資料必須在事後重新同步。
資料庫結構的定義不會被複寫,並且已發佈的資料表必須存在於訂閱的伺服器上。只有一般的資料表可以是複寫的標的。例如,您不能複寫到檢視表(view)。
這些資料表使用完全限定的資料表名稱,在發佈者和訂閱者之間進行匹配。不支援複寫到訂閱戶上不同名稱的資料表。
資料表的欄位也按照名稱對應,目標資料表中具有不同的欄位順序是允許的。欄位型別不需要完全相同,只要資料可以被轉換為目標類型即可。例如,你可以將型別為 integer 的欄位複寫至型別為 bigint 的欄位之中。目標資料表可以具有未由發佈資料表所提供的附加欄位。那些欄位將以它們的預設值填入。
如前所述,每個執行中的訂閱都會從遠端(發佈)端的複寫插槽接收變更。
Additional table synchronization slots are normally transient, created internally to perform initial table synchronization and dropped automatically when they are no longer needed. These table synchronization slots have generated names: “pg_%u_sync_%u_%llu
” (parameters: Subscription oid
, Table relid
, system identifier sysid
)
通常,使用 CREATE SUBSCRIPTION 建立訂閱時會自動建立遠端的複寫插槽,使用 DROP SUBSCRIPTION 移除訂閱時會自動移除該插槽。但是,在某些情況下,分別管理訂閱和底層複寫插槽可能很有用甚至是必要的。 以下是一些情況:
建立訂閱時,複寫插槽已經存在。在這種情況下,可以使用 create_slot = false 選項來與現有插槽關連以建立訂閱。
建立訂閱時,遠端主機無法存取或處於不明狀態。在這種情況下,可以使用 connect = false 選項建立訂閱。遠端主機將不會被聯繫。這是 pg_dump 所使用的。然後必須在訂閱啓動之前手動建立遠端的複寫插槽。
在移除訂閱時,應該保留複寫插槽。當使用者資料庫被移動到不同的主機並且從那裡被啓動時,這可能會有用。在這種情況下,在嘗試移除訂閱之前,請使用 ALTER SUBSCRIPTION 從插入位置解除關連。
在移除訂閱時,遠端主機無法存取。在這種情況下,在嘗試移除訂閱之前,請使用 ALTER SUBSCRIPTION 從插入位置解除關連。如果遠端資料庫服務不再存在,則不需要進一步的操作。但是,如果遠端資料庫服務不可存取,則應手動移除複寫插槽;否則它會繼續保留 WAL,最終可能會導致磁碟空間不足。應該仔細檢查這種情況。
Create some test tables on the publisher.
Create the same tables on the subscriber.
Insert data to the tables at the publisher side.
Create publications for the tables. The publications pub2
and pub3a
disallow some publish
operations. The publication pub3b
has a row filter (see Section 31.3).
Create subscriptions for the publications. The subscription sub3
subscribes to both pub3a
and pub3b
. All subscriptions will copy initial data by default.
Observe that initial table data is copied, regardless of the publish
operation of the publication.
Furthermore, because the initial data copy ignores the publish
operation, and because publication pub3a
has no row filter, it means the copied table t3
contains all rows even when they do not match the row filter of publication pub3b
.
Insert more data to the tables at the publisher side.
Now the publisher side data looks like:
Observe that during normal replication the appropriate publish
operations are used. This means publications pub2
and pub3a
will not replicate the INSERT
. Also, publication pub3b
will only replicate data that matches the row filter of pub3b
. Now the subscriber side data looks like:
By default, all data from all published tables will be replicated to the appropriate subscribers. The replicated data can be reduced by using a row filter. A user might choose to use row filters for behavioral, security or performance reasons. If a published table sets a row filter, a row is replicated only if its data satisfies the row filter expression. This allows a set of tables to be partially replicated. The row filter is defined per table. Use a WHERE
clause after the table name for each published table that requires data to be filtered out. The WHERE
clause must be enclosed by parentheses. See for details.
Row filters are applied before publishing the changes. If the row filter evaluates to false
or NULL
then the row is not replicated. The WHERE
clause expression is evaluated with the same role used for the replication connection (i.e. the role specified in the CONNECTION
clause of the ). Row filters have no effect for TRUNCATE
command.
The WHERE
clause allows only simple expressions. It cannot contain user-defined functions, operators, types, and collations, system column references or non-immutable built-in functions.
If a publication publishes UPDATE
or DELETE
operations, the row filter WHERE
clause must contain only columns that are covered by the replica identity (see ). If a publication publishes only INSERT
operations, the row filter WHERE
clause can use any column.
Whenever an UPDATE
is processed, the row filter expression is evaluated for both the old and new row (i.e. using the data before and after the update). If both evaluations are true
, it replicates the UPDATE
change. If both evaluations are false
, it doesn't replicate the change. If only one of the old/new rows matches the row filter expression, the UPDATE
is transformed to INSERT
or DELETE
, to avoid any data inconsistency. The row on the subscriber should reflect what is defined by the row filter expression on the publisher.
If the old row satisfies the row filter expression (it was sent to the subscriber) but the new row doesn't, then, from a data consistency perspective the old row should be removed from the subscriber. So the UPDATE
is transformed into a DELETE
.
If the old row doesn't satisfy the row filter expression (it wasn't sent to the subscriber) but the new row does, then, from a data consistency perspective the new row should be added to the subscriber. So the UPDATE
is transformed into an INSERT
.
summarizes the applied transformations.
UPDATE
Transformation SummaryOld row | New row | Transformation |
---|
If the publication contains a partitioned table, the publication parameter publish_via_partition_root
determines which row filter is used. If publish_via_partition_root
is true
, the root partitioned table's row filter is used. Otherwise, if publish_via_partition_root
is false
(default), each partition's row filter is used.
If the subscription requires copying pre-existing table data and a publication contains WHERE
clauses, only data that satisfies the row filter expressions is copied to the subscriber.
If the subscriber is in a release prior to 15, copy pre-existing data doesn't use row filters even if they are defined in the publication. This is because old releases can only copy the entire table data.
If the subscription has several publications in which the same table has been published with different row filters (for the same publish
operation), those expressions get ORed together, so that rows satisfying any of the expressions will be replicated. This means all the other row filters for the same table become redundant if:
One of the publications has no row filter.
One of the publications was created using FOR ALL TABLES
. This clause does not allow row filters.
One of the publications was created using FOR TABLES IN SCHEMA
and the table belongs to the referred schema. This clause does not allow row filters.
Create some tables to be used in the following examples.
Create some publications. Publication p1
has one table (t1
) and that table has a row filter. Publication p2
has two tables. Table t1
has no row filter, and table t2
has a row filter. Publication p3
has two tables, and both of them have a row filter.
psql
can be used to show the row filter expressions (if defined) for each publication.
psql
can be used to show the row filter expressions (if defined) for each table. See that table t1
is a member of two publications, but has a row filter only in p1
. See that table t2
is a member of two publications, and has a different row filter in each of them.
On the subscriber node, create a table t1
with the same definition as the one on the publisher, and also create the subscription s1
that subscribes to the publication p1
.
Insert some rows. Only the rows satisfying the t1 WHERE
clause of publication p1
are replicated.
Update some data, where the old and new row values both satisfy the t1 WHERE
clause of publication p1
. The UPDATE
replicates the change as normal.
Update some data, where the old row values did not satisfy the t1 WHERE
clause of publication p1
, but the new row values do satisfy it. The UPDATE
is transformed into an INSERT
and the change is replicated. See the new row on the subscriber.
Update some data, where the old row values satisfied the t1 WHERE
clause of publication p1
, but the new row values do not satisfy it. The UPDATE
is transformed into a DELETE
and the change is replicated. See that the row is removed from the subscriber.
The following examples show how the publication parameter publish_via_partition_root
determines whether the row filter of the parent or child table will be used in the case of partitioned tables.
Create a partitioned table on the publisher.
Create the same tables on the subscriber.
Create a publication p4
, and then subscribe to it. The publication parameter publish_via_partition_root
is set as true. There are row filters defined on both the partitioned table (parent
), and on the partition (child
).
Insert some values directly into the parent
and child
tables. They replicate using the row filter of parent
(because publish_via_partition_root
is true).
Repeat the same test, but with a different value for publish_via_partition_root
. The publication parameter publish_via_partition_root
is set as false. A row filter is defined on the partition (child
).
Do the inserts on the publisher same as before. They replicate using the row filter of child
(because publish_via_partition_root
is false).
Each publication can optionally specify which columns of each table are replicated to subscribers. The table on the subscriber side must have at least all the columns that are published. If no column list is specified, then all columns on the publisher are replicated. See for details on the syntax.
The choice of columns can be based on behavioral or performance reasons. However, do not rely on this feature for security: a malicious subscriber is able to obtain data from columns that are not specifically published. If security is a consideration, protections can be applied at the publisher side.
If no column list is specified, any columns added later are automatically replicated. This means that having a column list which names all columns is not the same as having no column list at all.
A column list can contain only simple column references. The order of columns in the list is not preserved.
Specifying a column list when the publication also publishes FOR TABLES IN SCHEMA
is not supported.
For partitioned tables, the publication parameter publish_via_partition_root
determines which column list is used. If publish_via_partition_root
is true
, the root partitioned table's column list is used. Otherwise, if publish_via_partition_root
is false
(the default), each partition's column list is used.
If a publication publishes UPDATE
or DELETE
operations, any column list must include the table's replica identity columns (see ). If a publication publishes only INSERT
operations, then the column list may omit replica identity columns.
Column lists have no effect for the TRUNCATE
command.
During initial data synchronization, only the published columns are copied. However, if the subscriber is from a release prior to 15, then all the columns in the table are copied during initial data synchronization, ignoring any column lists.
There's currently no support for subscriptions comprising several publications where the same table has been published with different column lists. disallows creating such subscriptions, but it is still possible to get into that situation by adding or altering column lists on the publication side after a subscription has been created.
This means changing the column lists of tables on publications that are already subscribed could lead to errors being thrown on the subscriber side.
If a subscription is affected by this problem, the only way to resume replication is to adjust one of the column lists on the publication side so that they all match; and then either recreate the subscription, or use ALTER SUBSCRIPTION ... DROP PUBLICATION
to remove one of the offending publications and add it again.
Create a table t1
to be used in the following example.
Create a publication p1
. A column list is defined for table t1
to reduce the number of columns that will be replicated. Notice that the order of column names in the column list does not matter.
psql
can be used to show the column lists (if defined) for each publication.
psql
can be used to show the column lists (if defined) for each table.
On the subscriber node, create a table t1
which now only needs a subset of the columns that were on the publisher table t1
, and also create the subscription s1
that subscribes to the publication p1
.
On the publisher node, insert some rows to table t1
.
Only data from the column list of publication p1
is replicated.
If the subscription has several publications in which a table has been published with different WHERE
clauses, rows that satisfy any of the expressions will be copied. See for details.
Because initial data synchronization does not take into account the publish
parameter when copying existing table data, some rows may be copied that would not be replicated using DML. Refer to , and see for examples.
no match | no match | don't replicate |
no match | match |
|
match | no match |
|
match | match |
|
首先在 postgresql.conf 中進行組態設定:
其他所需的設定的預設值已經足夠。
需要調整 pg_hba.conf 以允許複寫(這裡的內容取決於您想要用於連線的實際網路配置和使用者):
然後在發佈者的資料庫上:
在訂閱者的資料庫上:
以上將啟動複寫程序,此程序同步資料表 users 和 departments 的初始資料表內容,然後開始以增量變更複寫到這些資料表。
用於複寫連線的角色必須具有 REPLICATION 屬性(或者是超級使用者)。該角色的存取必須在 pg_hba.conf 中設定,並且必須具有 LOGIN 屬性。
為了能夠複製初始資料表資料,用於複寫連線的角色必須在發佈的資料表上具有 SELECT 權限(或者是超級使用者)。
要建立發佈,使用者必須在資料庫中具有 CREATE 權限。
要將資料表加到發佈中,使用者必須擁有該資料表的所有權。要建立自動發佈所有資料表的發佈,使用者必須是超級使用者。
要建立訂閱,使用者必須是超級使用者。
訂閱 apply 程序將以超級使用者的權限在本地資料庫中運行。
權限僅在複寫連線開始時檢查一次。由於每個變更記錄都是從發佈者處讀取的,因此不會重新檢查,也不會在套用每個變更時重新檢查。
邏輯複寫透過複寫發佈者資料庫上的資料快照開始。一旦完成,發佈者的變化就會即時發送給訂閱者。訂閱者按照對發佈者進行提交的順序套用資料,以確保任何單個訂閱中的發佈的交易事務一致性。
邏輯複寫採用類似於實體的串流複寫的體系結構構建立(請參閱第 26.2.5 節)。 它由「walsender」和「apply」過程實施。walsender 程序啟動 WAL 的邏輯解碼(在第 48 章中介紹)並載入標準的邏輯解碼套件(pgoutput)。該套件將從 WAL 讀取的變更轉換為邏輯複寫協定(請參閱第 52.5 節),並根據發佈規範過濾資料。然後使用串流複寫協定將資料連續傳輸到 apply 程序,後者將資料映射到本地資料表並按照正確的事務順序套用收到的各個變更。
使用者資料庫上的應用程序始終在將 session_replication_role 設定為 replica 的情況下運行,這會對事件觸發器和限制條件會產生影響。
邏輯複寫 apply 程序當下只觸發資料列觸發器,而不觸發查詢語句觸發器。但是,初始資料表同步會像 COPY 指令一樣執行,因此會觸發 INSERT 的資料列和查詢語句觸發器。
現有訂閱資料表中的初始資料被快照並複寫到特殊型別的 apply 程序的平行程序中。此過程將建立自己的臨時複寫插槽並複寫現有資料。一旦複寫了現有資料,程該序便進入同步模式,透過使用標準邏輯複寫對初始資料複寫過程中發生的任何變更進行串流傳輸,確保該資料表與主應用程序處於同步狀態。同步完成後,資料表的複寫控制將回到正常複寫繼續進行的主應用程序。
邏輯複寫的行為與正常的 DML 操作類似,因為即使在訂閱節點上變更了資料,資料也會更新。 如果傳入的資料違反任何限制條件,複寫將會停止。這被稱為衝突。當複寫 UPDATE 或 DELETE 操作時,失去的資料不會產生衝突,而這些操作將會簡單地跳過。
衝突會產生錯誤並停止複寫;它必須由使用者手動解決。有關衝突的詳細訊息可以在使用者的伺服器日誌中找到。
解決方案可以透過變更訂閱戶上的資料來完成,以避免與傳入變更衝突或跳過與現有資料衝突的交易事務。透過呼叫 pg_replication_origin_advance() 函數以及與訂閱名稱和位置相對應的 node_name,可以跳過該交易事務。可以在 pg_replication_origin_status 系統檢視表中看到開始的目前位置。