9.3 9.4 9.5 9.6 10 11 12 13 14 15
阿里云PostgreSQL 问题报告 纠错本页面

31.3. 行过滤器

31.3.1. 行过滤规则
31.3.2. 表达式限制
31.3.3. 更新转换
31.3.4. 分区表
31.3.5. 初始数据同步
31.3.6. 组合多行过滤器
31.3.7. 例子

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

31.3.1. 行过滤规则

行过滤器在发布更改之前应用。如果行过滤器评估为falseNULL, 则该行不会被复制。WHERE子句表达式将使用与复制连接相同的角色进行评估(即在 CONNECTION子句中指定的角色CREATE SUBSCRIPTION)。行过滤器对 TRUNCATE命令无效。

31.3.2. 表达式限制

WHERE子句只允许简单表达式。它不能包含用户定义的函数、操作符、类型和排序规则、系统列引用或非不可变内置函数。

如果一个发布包含UPDATEDELETE操作的出版物, 行过滤器WHERE子句必须只包含副本标识覆盖的列 (参见REPLICA IDENTITY)。如果一个发布仅发布 INSERT操作,行过滤器WHERE子句可以使用任何列。

31.3.3. 更新转换

每当处理UPDATE时,都会对旧行和新行的行过滤表达式进行评估(即使用更新前和更新后的数据)。 如果两次评估都为true,则复制UPDATE更改。 如果两次评估都为false,则不复制更改。 如果旧行/新行中只有一个与行过滤表达式匹配,则将UPDATE转换为INSERTDELETE, 以避免任何数据不一致。订阅者上的行应反映发布者上行过滤表达式定义的内容。

如果旧行满足行过滤表达式(已发送给订阅者),但新行不满足,则从数据一致性的角度来看, 应该从订阅者中删除旧行。因此UPDATE被转换为DELETE

如果旧行不满足行过滤表达式(它没有发送给订阅者),但新行满足,则从数据一致性的角度来看, 应将新行添加到订阅者。 因此,UPDATE被转换为INSERT

表 31.1 总结了应用的转换。

表 31.1. UPDATE 转换摘要

旧行新行转换
无匹配无匹配不复制
不匹配匹配INSERT
匹配不匹配DELETE
匹配匹配更新

31.3.4. 分区表

如果出版物包含分区表,则出版参数publish_via_partition_root确定使用哪个行过滤器。 如果publish_via_partition_roottrue,则使用根分区表的行过滤器。 否则,如果publish_via_partition_rootfalse(默认情况下),则使用每个分区的行过滤器。

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=# 创建表 t1(a int, b int, c text, PRIMARY KEY(a,c));
CREATE TABLE
test_pub=# 创建表 t2(d int, e int, f int, PRIMARY KEY(d));
CREATE TABLE
test_pub=# 创建表 t3(g int, h int, i int, PRIMARY KEY(g));
CREATE TABLE

创建一些出版物。出版物p1有一个表(t1),该表有一个行过滤器。 出版物p2有两个表。表t1没有行过滤器,表t2有一个行过滤器。 出版物p3有两个表,它们都有一个行过滤器。

test_pub=# 为表t1创建出版物p1,条件为(a > 5 AND c = 'NSW');
CREATE PUBLICATION
test_pub=# 为表t1、t2创建出版物p2,条件为(e = 99);
CREATE PUBLICATION
test_pub=# 为表t2创建出版物p3,条件为(d = 10),为表t3创建出版物,条件为(g = 10);
CREATE PUBLICATION

psql可以用来显示每个发布的行过滤表达式(如果已定义)。

test_pub=# \dRp+
                               发布 p1
  所有者   | 所有表 | 插入 | 更新 | 删除 | 截断 | 通过根
----------+--------+-----+-----+-----+-----+------
 postgres | f      | t   | t   | t   | t   | f
表:
    "public.t1" WHERE ((a > 5) AND (c = 'NSW'::text))

                               发布 p2
  所有者   | 所有表 | 插入 | 更新 | 删除 | 截断 | 通过根
----------+--------+-----+-----+-----+-----+------
 postgres | f      | t   | t   | t   | t   | f
表:
    "public.t1"
    "public.t2" WHERE (e = 99)

                               发布 p3
  所有者   | 所有表 | 插入 | 更新 | 删除 | 截断 | 通过根
----------+--------+-----+-----+-----+-----+------
 postgres | f      | t   | t   | t   | t   | f
表:
    "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)

更新一些数据,其中旧行和新行的值都满足发布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)

更新一些数据,其中旧行的值满足t1 WHERE子句的发布p1, 但新行的值不满足。 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)