2025年9月25日: PostgreSQL 18 釋出!
支援的版本:當前 (18) / 17 / 16 / 15
開發版本:devel

29.4. 行過濾器 #

預設情況下,所有已釋出表的所有資料都將複製到相應的訂閱者。可以透過使用行過濾器來減少複製的資料。使用者可能會出於行為、安全或效能原因選擇使用行過濾器。如果已釋出的表設定了行過濾器,則僅當行的資料滿足行過濾器表示式時,才會複製該行。這允許對一組表進行部分複製。行過濾器是按表定義的。對於每個需要過濾掉資料的已釋出表,請在表名後使用WHERE子句。 WHERE子句必須用括號括起來。有關詳細資訊,請參閱CREATE PUBLICATION

29.4.1. 行過濾器規則 #

行過濾器在釋出更改之前應用。如果行過濾器評估為falseNULL,則不會複製該行。 WHERE子句表示式使用用於複製連線的相同角色進行評估(即,在CREATE SUBSCRIPTIONCONNECTION子句中指定的作用域)。行過濾器對TRUNCATE命令無效。

29.4.2. 表示式限制 #

WHERE子句僅允許簡單表示式。它不能包含使用者定義的函式、運算子、型別和排序規則、系統列引用或非永續性內建函式。

如果釋出了UPDATEDELETE操作的釋出,則行過濾器WHERE子句只能包含副本標識所涵蓋的列(請參閱REPLICA IDENTITY)。如果釋出僅包含INSERT操作的釋出,則行過濾器WHERE子句可以使用任何列。

29.4.3. UPDATE 轉換 #

每當處理UPDATE時,都會對舊行和新行(即,使用更新之前和之後的資料)評估行過濾器表示式。如果兩個評估都為true,則會複製UPDATE更改。如果兩個評估都為false,則不會複製更改。只有舊行/新行中的一行匹配行過濾器表示式,UPDATE將轉換為INSERTDELETE,以避免任何資料不一致。訂閱者上的行應反映釋出者上的行過濾器表示式所定義的內容。

如果舊行滿足行過濾器表示式(已傳送到訂閱者),但新行不滿足,則從資料一致性的角度來看,應從訂閱者中刪除舊行。因此,UPDATE將轉換為DELETE

如果舊行不滿足行過濾器表示式(未傳送到訂閱者),但新行滿足,則從資料一致性的角度來看,應將新行新增到訂閱者。因此,UPDATE將轉換為INSERT

29.1總結了應用的轉換。

表 29.1. UPDATE 轉換摘要

舊行 新行 轉換
不匹配 不匹配 不復制
不匹配 匹配 INSERT
匹配 不匹配 DELETE
匹配 匹配 UPDATE

29.4.4. 分割槽表 #

如果釋出包含分割槽表,則釋出引數publish_via_partition_root決定了使用哪個行過濾器。如果publish_via_partition_roottrue,則使用根分割槽表的行過濾器。否則,如果publish_via_partition_rootfalse(預設值),則使用每個分割槽的行過濾器。

29.4.5. 初始資料同步 #

如果訂閱需要複製預先存在的資料並且釋出包含WHERE子句,則僅複製滿足行過濾器表示式的資料到訂閱者。

如果訂閱有多個釋出,其中一個表以不同的WHERE子句釋出,則將複製滿足任何表示式的行。有關詳細資訊,請參閱第 29.4.6 節

警告

由於初始資料同步在複製現有表資料時不會考慮publish引數,因此可能會複製一些不會使用 DML 複製的行。請參閱第 29.9.1 節,並參閱第 29.2.2 節中的示例。

注意

如果訂閱者是 15 版本之前的版本,則複製預先存在的資料即使在釋出中定義了行過濾器也不會使用它們。這是因為舊版本只能複製整個表資料。

29.4.6. 組合多個行過濾器 #

如果訂閱有多個釋出,其中同一個表已釋出(針對相同的publish操作)但具有不同的行過濾器,則這些表示式將透過 OR 組合在一起,以便複製滿足任何表示式的行。這意味著同一表的其他所有行過濾器都將變得冗餘,如果

  • 其中一個釋出沒有行過濾器。

  • 其中一個釋出是使用FOR ALL TABLES建立的。此子句不允許行過濾器。

  • 其中一個釋出是使用FOR TABLES IN SCHEMA建立的,並且表屬於引用的模式。此子句不允許行過濾器。

29.4.7. 示例 #

建立一些要在以下示例中使用到的表。

/* pub # */ CREATE TABLE t1(a int, b int, c text, PRIMARY KEY(a,c));
/* pub # */ CREATE TABLE t2(d int, e int, f int, PRIMARY KEY(d));
/* pub # */ CREATE TABLE t3(g int, h int, i int, PRIMARY KEY(g));

建立一些釋出。釋出p1有一個表(t1),該表有一個行過濾器。釋出p2有兩個表。表t1沒有行過濾器,表t2有一個行過濾器。釋出p3有兩個表,並且它們都有一個行過濾器。

/* pub # */ CREATE PUBLICATION p1 FOR TABLE t1 WHERE (a > 5 AND c = 'NSW');
/* pub # */ CREATE PUBLICATION p2 FOR TABLE t1, t2 WHERE (e = 99);
/* pub # */ CREATE PUBLICATION p3 FOR TABLE t2 WHERE (d = 10), t3 WHERE (g = 10);

psql 可用於顯示每個釋出的行過濾器表示式(如果已定義)。

/* pub # */ \dRp+
                                         Publication p1
  Owner   | All tables | Inserts | Updates | Deletes | Truncates | Generated columns | Via root
----------+------------+---------+---------+---------+-----------+-------------------+----------
 postgres | f          | t       | t       | t       | t         | none              | f
Tables:
    "public.t1" WHERE ((a > 5) AND (c = 'NSW'::text))

                                         Publication p2
  Owner   | All tables | Inserts | Updates | Deletes | Truncates | Generated columns | Via root
----------+------------+---------+---------+---------+-----------+-------------------+----------
 postgres | f          | t       | t       | t       | t         | none              | f
Tables:
    "public.t1"
    "public.t2" WHERE (e = 99)

                                         Publication p3
  Owner   | All tables | Inserts | Updates | Deletes | Truncates | Generated columns | Via root
----------+------------+---------+---------+---------+-----------+-------------------+----------
 postgres | f          | t       | t       | t       | t         | none              | f
Tables:
    "public.t2" WHERE (d = 10)
    "public.t3" WHERE (g = 10)

psql 可用於顯示每個表的行過濾器表示式(如果已定義)。請注意,表t1是兩個釋出的成員,但僅在p1中有一個行過濾器。請注意,表t2是兩個釋出的成員,並且在每個釋出中都有不同的行過濾器。

/* pub # */ \d t1
                 Table "public.t1"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 a      | integer |           | not null |
 b      | integer |           |          |
 c      | text    |           | not null |
Indexes:
    "t1_pkey" PRIMARY KEY, btree (a, c)
Publications:
    "p1" WHERE ((a > 5) AND (c = 'NSW'::text))
    "p2"

/* pub # */ \d t2
                 Table "public.t2"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 d      | integer |           | not null |
 e      | integer |           |          |
 f      | integer |           |          |
Indexes:
    "t2_pkey" PRIMARY KEY, btree (d)
Publications:
    "p2" WHERE (e = 99)
    "p3" WHERE (d = 10)

/* pub # */ \d t3
                 Table "public.t3"
 Column |  Type   | Collation | Nullable | Default
--------+---------+-----------+----------+---------
 g      | integer |           | not null |
 h      | integer |           |          |
 i      | integer |           |          |
Indexes:
    "t3_pkey" PRIMARY KEY, btree (g)
Publications:
    "p3" WHERE (g = 10)

在訂閱者節點上,建立一個與釋出者上的表定義相同的表t1,並建立訂閱s1以訂閱釋出p1

/* sub # */ CREATE TABLE t1(a int, b int, c text, PRIMARY KEY(a,c));
/* sub # */ CREATE SUBSCRIPTION s1
/* sub - */ CONNECTION 'host=localhost dbname=test_pub application_name=s1'
/* sub - */ PUBLICATION p1;

插入一些行。僅複製滿足釋出p1t1 WHERE子句的行。

/* pub # */ INSERT INTO t1 VALUES (2, 102, 'NSW');
/* pub # */ INSERT INTO t1 VALUES (3, 103, 'QLD');
/* pub # */ INSERT INTO t1 VALUES (4, 104, 'VIC');
/* pub # */ INSERT INTO t1 VALUES (5, 105, 'ACT');
/* pub # */ INSERT INTO t1 VALUES (6, 106, 'NSW');
/* pub # */ INSERT INTO t1 VALUES (7, 107, 'NT');
/* pub # */ INSERT INTO t1 VALUES (8, 108, 'QLD');
/* pub # */ INSERT INTO t1 VALUES (9, 109, 'NSW');

/* pub # */ SELECT * FROM t1;
 a |  b  |  c
---+-----+-----
 2 | 102 | NSW
 3 | 103 | QLD
 4 | 104 | VIC
 5 | 105 | ACT
 6 | 106 | NSW
 7 | 107 | NT
 8 | 108 | QLD
 9 | 109 | NSW
(8 rows)
/* sub # */ SELECT * FROM t1;
 a |  b  |  c
---+-----+-----
 6 | 106 | NSW
 9 | 109 | NSW
(2 rows)

更新一些資料,其中舊行和新行的值都滿足釋出p1t1 WHERE子句。 UPDATE正常複製更改。

/* pub # */ UPDATE t1 SET b = 999 WHERE a = 6;

/* pub # */ SELECT * FROM t1;
 a |  b  |  c
---+-----+-----
 2 | 102 | NSW
 3 | 103 | QLD
 4 | 104 | VIC
 5 | 105 | ACT
 7 | 107 | NT
 8 | 108 | QLD
 9 | 109 | NSW
 6 | 999 | NSW
(8 rows)
/* sub # */ SELECT * FROM t1;
 a |  b  |  c
---+-----+-----
 9 | 109 | NSW
 6 | 999 | NSW
(2 rows)

更新一些資料,其中舊行值不滿足釋出p1t1 WHERE子句,但新行值滿足。 UPDATE轉換為INSERT,並複製更改。請參閱訂閱者上的新行。

/* pub # */ UPDATE t1 SET a = 555 WHERE a = 2;

/* pub # */ SELECT * FROM t1;
  a  |  b  |  c
-----+-----+-----
   3 | 103 | QLD
   4 | 104 | VIC
   5 | 105 | ACT
   7 | 107 | NT
   8 | 108 | QLD
   9 | 109 | NSW
   6 | 999 | NSW
 555 | 102 | NSW
(8 rows)
/* sub # */ SELECT * FROM t1;
  a  |  b  |  c
-----+-----+-----
   9 | 109 | NSW
   6 | 999 | NSW
 555 | 102 | NSW
(3 rows)

更新一些資料,其中舊行值滿足釋出p1t1 WHERE子句,但新行值不滿足。 UPDATE轉換為DELETE,並複製更改。請注意,該行已從訂閱者中刪除。

/* pub # */ UPDATE t1 SET c = 'VIC' WHERE a = 9;

/* pub # */ SELECT * FROM t1;
  a  |  b  |  c
-----+-----+-----
   3 | 103 | QLD
   4 | 104 | VIC
   5 | 105 | ACT
   7 | 107 | NT
   8 | 108 | QLD
   6 | 999 | NSW
 555 | 102 | NSW
   9 | 109 | VIC
(8 rows)
/* sub # */ SELECT * FROM t1;
  a  |  b  |  c
-----+-----+-----
   6 | 999 | NSW
 555 | 102 | NSW
(2 rows)

以下示例說明了釋出引數publish_via_partition_root如何確定在分割槽表的情況下使用父表或子表的行過濾器。

在釋出者上建立分割槽表。

/* pub # */ CREATE TABLE parent(a int PRIMARY KEY) PARTITION BY RANGE(a);
/* pub # */ CREATE TABLE child PARTITION OF parent DEFAULT;

在訂閱者上建立相同的表。

/* sub # */ CREATE TABLE parent(a int PRIMARY KEY) PARTITION BY RANGE(a);
/* sub # */ CREATE TABLE child PARTITION OF parent DEFAULT;

建立釋出p4,然後訂閱它。釋出引數publish_via_partition_root設定為true。在分割槽表(parent)和分割槽(child)上都定義了行過濾器。

/* pub # */ CREATE PUBLICATION p4 FOR TABLE parent WHERE (a < 5), child WHERE (a >= 5)
/* pub - */ WITH (publish_via_partition_root=true);
/* sub # */ CREATE SUBSCRIPTION s4
/* sub - */ CONNECTION 'host=localhost dbname=test_pub application_name=s4'
/* sub - */ PUBLICATION p4;

直接在parentchild表中插入一些值。它們使用parent的行過濾器進行復制(因為publish_via_partition_root為true)。

/* pub # */ INSERT INTO parent VALUES (2), (4), (6);
/* pub # */ INSERT INTO child VALUES (3), (5), (7);

/* pub # */ SELECT * FROM parent ORDER BY a;
 a
---
 2
 3
 4
 5
 6
 7
(6 rows)
/* sub # */ SELECT * FROM parent ORDER BY a;
 a
---
 2
 3
 4
(3 rows)

使用不同的publish_via_partition_root值重複相同的測試。釋出引數publish_via_partition_root設定為false。在分割槽(child)上定義了行過濾器。

/* pub # */ DROP PUBLICATION p4;
/* pub # */ CREATE PUBLICATION p4 FOR TABLE parent, child WHERE (a >= 5)
/* pub - */ WITH (publish_via_partition_root=false);
/* sub # */ ALTER SUBSCRIPTION s4 REFRESH PUBLICATION;

在釋出者上插入與之前相同的數值。它們使用child的行過濾器進行復制(因為publish_via_partition_root為false)。

/* pub # */ TRUNCATE parent;
/* pub # */ INSERT INTO parent VALUES (2), (4), (6);
/* pub # */ INSERT INTO child VALUES (3), (5), (7);

/* pub # */ SELECT * FROM parent ORDER BY a;
 a
---
 2
 3
 4
 5
 6
 7
(6 rows)
/* sub # */ SELECT * FROM child ORDER BY a;
 a
---
 5
 6
 7
(3 rows)

提交更正

如果您在文件中看到任何不正確的內容、與您對特定功能的體驗不符的內容或需要進一步澄清的內容,請使用此表單報告文件問題。