Redrock Postgres 搜索 英文
版本: 15 / 16 / 17

29.4. 行过滤器 #

29.4.1. 行过滤器规则
29.4.2. 表达式限制
29.4.3. UPDATE 变换
29.4.4. 分区表
29.4.5. 初始数据同步
29.4.6. 组合多个行过滤器
29.4.7. 示例

默认情况下,所有表的所有数据都将复制到适当的订阅者。可以通过使用行过滤器来减少复制的数据。用户可以选择出于行为、安全或性能方面的考虑,来使用行过滤器。如果已发布表设置了行过滤器,仅当其数据满足行过滤器表达式时才复制一行。这允许部分复制一组表。行过滤器根据表定义。对于要求过滤掉数据的每个已发布表,都在表名后使用 WHERE 子句。 WHERE 子句必须用括号括起来。有关详细信息,请参见CREATE PUBLICATION

29.4.1. 行过滤器规则 #

行筛选器在发布更改之前应用。如果行筛选器评估为 falseNULL 则不会复制行。使用与复制连接相同的角色评估 WHERE 子句表达式(即 CONNECTION 子句中 创建订阅中指定的角色)。行筛选器对 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 转换摘要

旧行 新行 转换
无匹配 无匹配 不复制
无匹配 匹配 插入
匹配 无匹配 删除
匹配 匹配 更新

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.8.1 节,并参阅 第 29.2.2 节 了解示例。

注意

如果订阅者处于 15 之前的版本中,即使在出版物中定义了行筛选器,复制已存在数据也不会使用这些筛选器。这是因为旧版本只能复制整个表数据。

29.4.6. 合并多个行筛选器 #

如果订阅有具有不同行筛选器(针对相同 publish 操作)发布相同表的多个出版物,则这些表达式会连接到一起,以便满足表达式 任何 的都会被复制的行。这意味着如果

  • 有一篇出版物没有行筛选器。

  • 使用 FOR ALL TABLES 创建了一篇出版物。此子句不允许使用行筛选器。

  • 使用 FOR TABLES IN SCHEMA 创建了一篇出版物,表属于引用的架构。此子句不允许使用行筛选器。

29.4.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,以及创建订阅了发布 p1 的订阅 s1

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

插入一些行。只有满足发布 p1t1 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)

更新一些满足发布 p1t1 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)

更新一些数据,旧行值不满足发布 p1t1 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)

更新一些数据,旧行值满足发布 p1t1 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

直接在 parentchild 表中插入一些值。它们将使用 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)