默认情况下,所有已发布表的所有数据都将复制到相应的订阅者。可以通过使用行过滤器来减少复制的数据。用户可能会出于行为、安全或性能原因选择使用行过滤器。如果已发布的表设置了行过滤器,则仅当行的数据满足行过滤器表达式时,才会复制该行。这允许对一组表进行部分复制。行过滤器是按表定义的。对于每个需要过滤掉数据的已发布表,请在表名后使用WHERE
子句。 WHERE
子句必须用括号括起来。有关详细信息,请参阅CREATE PUBLICATION。
行过滤器在发布更改之前应用。如果行过滤器评估为false
或NULL
,则不会复制该行。 WHERE
子句表达式使用用于复制连接的相同角色进行评估(即,在CREATE SUBSCRIPTION的CONNECTION
子句中指定的作用域)。行过滤器对TRUNCATE
命令无效。
WHERE
子句仅允许简单表达式。它不能包含用户定义的函数、运算符、类型和排序规则、系统列引用或非持久性内置函数。
如果发布了UPDATE
或DELETE
操作的发布,则行过滤器WHERE
子句只能包含副本标识所涵盖的列(请参阅REPLICA IDENTITY
)。如果发布仅包含INSERT
操作的发布,则行过滤器WHERE
子句可以使用任何列。
每当处理UPDATE
时,都会对旧行和新行(即,使用更新之前和之后的数据)评估行过滤器表达式。如果两个评估都为true
,则会复制UPDATE
更改。如果两个评估都为false
,则不会复制更改。只有旧行/新行中的一行匹配行过滤器表达式,UPDATE
将转换为INSERT
或DELETE
,以避免任何数据不一致。订阅者上的行应反映发布者上的行过滤器表达式所定义的内容。
如果旧行满足行过滤器表达式(已发送到订阅者),但新行不满足,则从数据一致性的角度来看,应从订阅者中删除旧行。因此,UPDATE
将转换为DELETE
。
如果旧行不满足行过滤器表达式(未发送到订阅者),但新行满足,则从数据一致性的角度来看,应将新行添加到订阅者。因此,UPDATE
将转换为INSERT
。
表29.1总结了应用的转换。
表 29.1. UPDATE
转换摘要
旧行 | 新行 | 转换 |
---|---|---|
不匹配 | 不匹配 | 不复制 |
不匹配 | 匹配 | INSERT |
匹配 | 不匹配 | DELETE |
匹配 | 匹配 | UPDATE |
如果发布包含分区表,则发布参数publish_via_partition_root
决定了使用哪个行过滤器。如果publish_via_partition_root
为true
,则使用根分区表的行过滤器。否则,如果publish_via_partition_root
为false
(默认值),则使用每个分区的行过滤器。
如果订阅需要复制预先存在的数据并且发布包含WHERE
子句,则仅复制满足行过滤器表达式的数据到订阅者。
如果订阅有多个发布,其中一个表以不同的WHERE
子句发布,则将复制满足任何表达式的行。有关详细信息,请参阅第 29.4.6 节。
由于初始数据同步在复制现有表数据时不会考虑publish
参数,因此可能会复制一些不会使用 DML 复制的行。请参阅第 29.9.1 节,并参阅第 29.2.2 节中的示例。
如果订阅者是 15 版本之前的版本,则复制预先存在的数据即使在发布中定义了行过滤器也不会使用它们。这是因为旧版本只能复制整个表数据。
如果订阅有多个发布,其中同一个表已发布(针对相同的publish
操作)但具有不同的行过滤器,则这些表达式将通过 OR 组合在一起,以便复制满足任何表达式的行。这意味着同一表的其他所有行过滤器都将变得冗余,如果
其中一个发布没有行过滤器。
其中一个发布是使用FOR ALL TABLES
创建的。此子句不允许行过滤器。
其中一个发布是使用FOR TABLES IN SCHEMA
创建的,并且表属于引用的模式。此子句不允许行过滤器。
创建一些要在以下示例中使用到的表。
/* 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;
插入一些行。仅复制满足发布p1
的t1 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)
更新一些数据,其中旧行和新行的值都满足发布p1
的t1 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)
更新一些数据,其中旧行值不满足发布p1
的t1 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)
更新一些数据,其中旧行值满足发布p1
的t1 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;
直接在parent
和child
表中插入一些值。它们使用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)
如果您在文档中看到任何不正确的内容、与您对特定功能的体验不符的内容或需要进一步澄清的内容,请使用此表单报告文档问题。