31.3. 行过滤器#
默认情况下,所有已发布表中的所有数据都将复制到相应的订阅者。可以使用行过滤器来减少复制的数据。用户可能出于行为、安全或性能原因选择使用行过滤器。如果已发布表设置了行过滤器,则仅当其数据满足行过滤器表达式时才会复制该行。这允许部分复制一组表。行过滤器是针对每个表定义的。对于需要筛选数据的每个已发布表,请在表名后使用WHERE
子句。WHERE
子句必须用括号括起来。有关详细信息,请参见CREATE PUBLICATION。
31.3.1. 行过滤器规则#
行过滤器在发布更改之前应用。如果行过滤器求值为false
或NULL
,则不会复制该行。WHERE
子句表达式使用与复制连接相同的角色进行求值(即CONNECTION
子句中指定的CREATE SUBSCRIPTION中的角色)。行过滤器对TRUNCATE
命令没有影响。
31.3.2. 表达式限制#
WHERE
子句仅允许简单的表达式。它不能包含用户定义函数、运算符、类型和排序规则、系统列引用或非不可变内置函数。
如果发布发布UPDATE
或DELETE
操作,则行筛选器WHERE
子句必须仅包含副本标识覆盖的列(请参阅REPLICA IDENTITY
)。如果发布仅发布INSERT
操作,则行筛选器WHERE
子句可以使用任何列。
31.3.3. UPDATE 转换#
每当处理UPDATE
时,都会针对旧行和新行(即使用更新前后的数据)计算行筛选器表达式。如果两个计算结果均为true
,它将复制UPDATE
更改。如果两个计算结果均为false
,它将不会复制更改。如果仅旧/新行之一与行筛选器表达式匹配,则UPDATE
将转换为INSERT
或DELETE
,以避免任何数据不一致。订阅者上的行应反映发布者上的行筛选器表达式定义的内容。
如果旧行满足行筛选器表达式(已发送给订阅者),但新行不满足,那么从数据一致性的角度来看,应从订阅者中删除旧行。因此,UPDATE
将转换为DELETE
。
如果旧行不满足行筛选器表达式(未发送给订阅者),但新行满足,那么从数据一致性的角度来看,应将新行添加到订阅者。因此,UPDATE
将转换为INSERT
。
表 31.1总结了应用的转换。
表 31.1.UPDATE
转换摘要
旧行 | 新行 | 转换 |
---|---|---|
无匹配项 | 无匹配项 | 不复制 |
无匹配项 | 匹配 | 插入 |
匹配 | 无匹配项 | 删除 |
匹配 | 匹配 | 更新 |
31.3.4. 分区表#
如果发布包含分区表,则发布参数publish_via_partition_root
将确定使用哪种行筛选器。如果publish_via_partition_root
为true
,则将使用根分区表的行筛选器。否则,如果publish_via_partition_root
为false
(默认值),则将使用每个分区的行筛选器。
31.3.5. 初始数据同步#
如果订阅需要复制已存在表数据,并且发布包含WHERE
子句,则只有满足行筛选器表达式的才会复制到订阅者。
如果订阅在其中一个表已使用不同的WHERE
子句发布的多个发布中,则满足任何表达式的行都将被复制。有关详细信息,请参见第 31.3.6 节。
警告
由于初始数据同步在复制现有表数据时不考虑publish
参数,因此可能会复制一些不会使用 DML 复制的行。请参阅第 31.7.1 节,并参阅第 31.2.2 节中的示例。
注意
如果订阅者低于 15 版,则即使在发布中定义了行筛选器,复制已存在数据也不会使用行筛选器。这是因为旧版本只能复制整个表数据。
31.3.6. 合并多个行筛选器#
如果订阅中有多个发布,其中同一个表已使用不同的行筛选器发布(对于相同的publish
操作),这些表达式将通过 OR 运算符组合在一起,以便满足任何表达式的行都将被复制。这意味着如果
其中一个发布没有行筛选器。
其中一个发布是使用
FOR ALL TABLES
创建的。此子句不允许使用行筛选器。其中一个发布是使用
FOR TABLES IN SCHEMA
创建的,并且该表属于所引用的架构。此子句不允许使用行筛选器。
31.3.7. 示例#
创建一些表以用于以下示例。
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)