Postgresql 中文操作指南

31.3. Row Filters #

默认情况下,从所有已发布表的全部数据都将复制到相应的订阅者。可以通过使用 row filter 来减少复制的数据。用户可能会出于行为、安全或性能原因选择使用行过滤器。如果已发布表设置了行过滤器,只有当一行的数据满足行过滤器表达式时,该行才会被复制。这允许对一组表进行部分复制。行过滤器是按表定义的。对于需要将数据筛选出的每个已发布表,请在表名后使用 WHERE 子句。 WHERE 子句必须用括号括起来。有关详细信息,请参见 CREATE PUBLICATION

31.3.1. Row Filter Rules #

在发布更改时应用行过滤器 before 。如果行过滤器求值为 falseNULL ,则不会复制该行。使用用于复制连接(即 CREATE SUBSCRIPTIONCONNECTION 子句中指定的角色)的相同角色评估 WHERE 子句表达式。行过滤器对 TRUNCATE 命令没有影响。

31.3.2. Expression Restrictions #

_WHERE_子句仅允许简单表达式。它不能包含用户定义的函数、运算符、类型和校对、系统列引用或不可变的内置函数。

如果发布发布了 UPDATEDELETE 操作,则行过滤器 WHERE 子句只能包含复制标识覆盖的列(参见 REPLICA IDENTITY )。如果发布仅发布 INSERT 操作,则行过滤器 WHERE 子句可以使用任何列。

31.3.3. UPDATE Transformations #

每当处理_UPDATE_时,都会针对新旧两行计算行过滤器表达式(即,使用更新之前和更新之后的数据)。如果两个计算结果都为_true_,则复制_UPDATE_更改。如果两个计算结果都为_false_,则不复制更改。如果只有新旧行之一与行过滤器表达式匹配,则_UPDATE_转换为_INSERT_或_DELETE_,以避免任何数据不一致。订阅者上的行应反映发布者上行过滤器表达式的定义。

如果旧行满足行过滤器表达式(已发送给订阅者)但新行不满足,那么从数据一致性的角度来看,应从订阅者中删除旧行。所以_UPDATE_被转换为_DELETE_。

如果旧行不满足行过滤器表达式(未发送给订阅者)但新行满足,那么从数据一致性的角度来看,应将新行添加到订阅者中。所以_UPDATE_被转换为_INSERT_。

Table 31.1总结了应用的转换。

Table 31.1. UPDATE Transformation Summary

Old row

New row

Transformation

no match

no match

don’t replicate

no match

match

INSERT

match

no match

DELETE

match

match

UPDATE

31.3.4. Partitioned Tables #

如果发布包含分区表,则发布参数 publish_via_partition_root 确定使用哪个行过滤器。如果 publish_via_partition_roottrue ,则使用 root partitioned table’s 行过滤器。否则,如果 publish_via_partition_rootfalse (默认值),则使用每个 partition’s 行过滤器。

31.3.5. Initial Data Synchronization #

如果订阅需要进行现有表数据的复制,并且发布中包含_WHERE_子句,则只有满足行过滤器表达式的才能复制到订阅者中。

如果订阅中有多个发布,其中表已使用不同的_WHERE_从句发布,则将复制满足表达式_any_的行。有关详情,请参阅 Section 31.3.6

Warning

由于初始数据同步在复制现有表数据时不考虑 publish 参数,因此可能会复制一些使用 DML 不会复制的行。请参阅 Section 31.7.1 ,并参见 Section 31.2.2 以获取示例。

Note

如果订阅者处于 15 之前的版本中,则复制已存在的数据不会使用行过滤器,即使它们在发布物中已定义。这是因为旧版本只能复制整个表数据。

31.3.6. Combining Multiple Row Filters #

如果某个订阅中的多个出版物中发布了具有不同行筛选器的相同表(对于相同的 publish 操作),这些表达式将按 OR 组合,以便满足这些表达式中的 any 的行得到复制。这意味着如果出现以下情况,同一表的所有其他行筛选器将变为冗余:

31.3.7. Examples #

创建一些表用于以下示例中。

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)

更新一些数据,原有行和新行值均满足发布 t1 WHEREp1 子句。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)

更新一些数据,原行值未满足发布 t1 WHEREp1 子句,但新行值满足。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)

更新一些数据,原行值满足发布 t1 WHEREp1 子句,但新行值不满足。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。在分区表和分区中定义了行筛选器(parentchild)。

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)