默认情况下,所有已发布表的所有数据都将被复制到相应的订阅者。可以使用行过滤器来减少复制的数据。用户可能会出于行为、安全或性能原因选择使用行过滤器。如果已发布的表设置了行过滤器,则只有当其数据满足行过滤器表达式时,才会复制该行。这允许部分复制一组表。行过滤器是按表定义的。对于每个需要过滤掉数据的已发布表,请在表名后使用 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.8.1 节,并查看第 29.2.2 节中的示例。
如果订阅者是 15 之前的版本,则复制预先存在的数据时不会使用行过滤器,即使它们在发布中定义也是如此。这是因为旧版本只能复制整个表数据。
如果订阅有多个发布,其中同一个表已使用不同的行过滤器发布(对于相同的 publish 操作),则这些表达式会进行“或”运算,以便复制满足任何表达式的行。这意味着如果出现以下情况,则同一表的所有其他行过滤器都将变为多余:
其中一个发布没有行过滤器。
其中一个发布是使用 FOR ALL TABLES 创建的。此子句不允许行过滤器。
其中一个发布是使用 FOR TABLES IN SCHEMA 创建的,并且该表属于引用的模式。此子句不允许行过滤器。
创建一些表以在以下示例中使用。
test_pub=# CREATE TABLE t1(a int, b int, c text, PRIMARY KEY(a,c)); CREATE TABLE test_pub=# CREATE TABLE t2(d int, e int, f int, PRIMARY KEY(d)); CREATE TABLE test_pub=# CREATE TABLE t3(g int, h int, i int, PRIMARY KEY(g)); CREATE TABLE
创建一些发布。发布 p1 有一个表 (t1),并且该表具有行过滤器。发布 p2 有两个表。表 t1 没有行过滤器,而表 t2 有一个行过滤器。发布 p3 有两个表,并且它们都具有行过滤器。
test_pub=# CREATE PUBLICATION p1 FOR TABLE t1 WHERE (a > 5 AND c = 'NSW'); CREATE PUBLICATION test_pub=# CREATE PUBLICATION p2 FOR TABLE t1, t2 WHERE (e = 99); CREATE PUBLICATION test_pub=# CREATE PUBLICATION p3 FOR TABLE t2 WHERE (d = 10), t3 WHERE (g = 10); CREATE PUBLICATION
可以使用 psql 来显示每个发布的行过滤器表达式(如果已定义)。
test_pub=# \dRp+
Publication p1
Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
----------+------------+---------+---------+---------+-----------+----------
postgres | f | t | t | t | t | f
Tables:
"public.t1" WHERE ((a > 5) AND (c = 'NSW'::text))
Publication p2
Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
----------+------------+---------+---------+---------+-----------+----------
postgres | f | t | t | t | t | f
Tables:
"public.t1"
"public.t2" WHERE (e = 99)
Publication p3
Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
----------+------------+---------+---------+---------+-----------+----------
postgres | f | t | t | t | t | f
Tables:
"public.t2" WHERE (d = 10)
"public.t3" WHERE (g = 10)
可以使用 psql 来显示每个表的行过滤器表达式(如果已定义)。查看表 t1 是两个发布的成员,但在 p1 中只有一个行过滤器。查看表 t2 是两个发布的成员,并且在每个发布中都有不同的行过滤器。
test_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"
test_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)
test_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。
test_sub=# CREATE TABLE t1(a int, b int, c text, PRIMARY KEY(a,c)); CREATE TABLE test_sub=# CREATE SUBSCRIPTION s1 test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=s1' test_sub-# PUBLICATION p1; CREATE SUBSCRIPTION
插入一些行。仅复制满足发布 p1 的 t1 WHERE 子句的行。
test_pub=# INSERT INTO t1 VALUES (2, 102, 'NSW'); INSERT 0 1 test_pub=# INSERT INTO t1 VALUES (3, 103, 'QLD'); INSERT 0 1 test_pub=# INSERT INTO t1 VALUES (4, 104, 'VIC'); INSERT 0 1 test_pub=# INSERT INTO t1 VALUES (5, 105, 'ACT'); INSERT 0 1 test_pub=# INSERT INTO t1 VALUES (6, 106, 'NSW'); INSERT 0 1 test_pub=# INSERT INTO t1 VALUES (7, 107, 'NT'); INSERT 0 1 test_pub=# INSERT INTO t1 VALUES (8, 108, 'QLD'); INSERT 0 1 test_pub=# INSERT INTO t1 VALUES (9, 109, 'NSW'); INSERT 0 1 test_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)
test_sub=# SELECT * FROM t1; a | b | c ---+-----+----- 6 | 106 | NSW 9 | 109 | NSW (2 rows)
更新一些数据,其中旧行值和新行值都满足发布 p1 的 t1 WHERE 子句。UPDATE 会像往常一样复制更改。
test_pub=# UPDATE t1 SET b = 999 WHERE a = 6; UPDATE 1 test_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)
test_sub=# SELECT * FROM t1; a | b | c ---+-----+----- 9 | 109 | NSW 6 | 999 | NSW (2 rows)
更新一些数据,其中旧行值不满足发布 p1 的 t1 WHERE 子句,但新行值满足。 UPDATE 被转换为 INSERT,并且更改被复制。请参见订阅者上的新行。
test_pub=# UPDATE t1 SET a = 555 WHERE a = 2; UPDATE 1 test_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)
test_sub=# SELECT * FROM t1; a | b | c -----+-----+----- 9 | 109 | NSW 6 | 999 | NSW 555 | 102 | NSW (3 rows)
更新一些数据,其中旧行值满足发布 p1 的 t1 WHERE 子句,但新行值不满足。 UPDATE 被转换为 DELETE,并且更改被复制。请参见该行已从订阅者中删除。
test_pub=# UPDATE t1 SET c = 'VIC' WHERE a = 9; UPDATE 1 test_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)
test_sub=# SELECT * FROM t1; a | b | c -----+-----+----- 6 | 999 | NSW 555 | 102 | NSW (2 rows)
以下示例说明在分区表的情况下,发布参数 publish_via_partition_root 如何确定将使用父表还是子表的行过滤器。
在发布者上创建一个分区表。
test_pub=# CREATE TABLE parent(a int PRIMARY KEY) PARTITION BY RANGE(a); CREATE TABLE test_pub=# CREATE TABLE child PARTITION OF parent DEFAULT; CREATE TABLE
在订阅者上创建相同的表。
test_sub=# CREATE TABLE parent(a int PRIMARY KEY) PARTITION BY RANGE(a); CREATE TABLE test_sub=# CREATE TABLE child PARTITION OF parent DEFAULT; CREATE TABLE
创建一个发布 p4,然后订阅它。发布参数 publish_via_partition_root 设置为 true。在分区表 (parent) 和分区 (child) 上都定义了行过滤器。
test_pub=# CREATE PUBLICATION p4 FOR TABLE parent WHERE (a < 5), child WHERE (a >= 5) test_pub-# WITH (publish_via_partition_root=true); CREATE PUBLICATION
test_sub=# CREATE SUBSCRIPTION s4 test_sub-# CONNECTION 'host=localhost dbname=test_pub application_name=s4' test_sub-# PUBLICATION p4; CREATE SUBSCRIPTION
直接将一些值插入 parent 和 child 表中。它们使用 parent 的行过滤器复制(因为 publish_via_partition_root 为 true)。
test_pub=# INSERT INTO parent VALUES (2), (4), (6); INSERT 0 3 test_pub=# INSERT INTO child VALUES (3), (5), (7); INSERT 0 3 test_pub=# SELECT * FROM parent ORDER BY a; a --- 2 3 4 5 6 7 (6 rows)
test_sub=# SELECT * FROM parent ORDER BY a; a --- 2 3 4 (3 rows)
重复相同的测试,但使用不同的 publish_via_partition_root 值。发布参数 publish_via_partition_root 设置为 false。在分区 (child) 上定义了行过滤器。
test_pub=# DROP PUBLICATION p4; DROP PUBLICATION test_pub=# CREATE PUBLICATION p4 FOR TABLE parent, child WHERE (a >= 5) test_pub-# WITH (publish_via_partition_root=false); CREATE PUBLICATION
test_sub=# ALTER SUBSCRIPTION s4 REFRESH PUBLICATION; ALTER SUBSCRIPTION
像以前一样在发布者上执行插入。它们使用 child 的行过滤器复制(因为 publish_via_partition_root 为 false)。
test_pub=# TRUNCATE parent; TRUNCATE TABLE test_pub=# INSERT INTO parent VALUES (2), (4), (6); INSERT 0 3 test_pub=# INSERT INTO child VALUES (3), (5), (7); INSERT 0 3 test_pub=# SELECT * FROM parent ORDER BY a; a --- 2 3 4 5 6 7 (6 rows)
test_sub=# SELECT * FROM child ORDER BY a; a --- 5 6 7 (3 rows)
如果您在文档中看到任何不正确、与您使用特定功能的经验不符或需要进一步澄清的内容,请使用此表单报告文档问题。