行过滤器

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子句,且子句必须用括号括起。详情参见CREATE PUBLICATION。

29.4.1 行过滤规则

行过滤在发布变更之前应用。表达式求值为false或NULL时,该行不会复制。WHERE表达式以复制连接所用的角色求值,即CREATE SUBSCRIPTION的CONNECTION子句指定的角色。行过滤器对TRUNCATE命令不起作用。

29.4.2 表达式限制

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

如果发布包括UPDATE或DELETE,行过滤器的WHERE子句只能使用副本标识覆盖的列,参见REPLICA IDENTITY。如果发布仅包括INSERT,则WHERE子句可以使用任意列。

29.4.3 UPDATE 转换

处理UPDATE时,系统分别针对旧行和新行(即更新前、更新后的数据)计算过滤表达式。如果两次均为true,则复制UPDATE;如果两次均为false,则不复制。如果只有旧行或新行中的一方满足表达式,UPDATE会转换为INSERT或DELETE,以避免数据不一致。订阅端的数据应当反映发布端过滤表达式定义的结果。

如果旧行满足表达式、已发送到订阅端,而新行不满足,从一致性角度看,就应从订阅端移除旧行。因此UPDATE转换为DELETE。

如果旧行不满足表达式、未发送到订阅端,而新行满足,就应把新行加入订阅端。因此UPDATE转换为INSERT。

表29.1汇总了这些转换。

表29.1 UPDATE转换汇总

旧行 新行 转换结果
不匹配 不匹配 不复制
不匹配 匹配 INSERT
匹配 不匹配 DELETE
匹配 匹配 UPDATE

29.4.4 分区表

发布包含分区表时,publish_via_partition_root决定使用哪个行过滤器。为true时使用根分区表的过滤器;为false(默认值)时使用各个分区的过滤器。

29.4.5 初始数据同步

如果订阅需要复制已有表数据,而发布包含WHERE子句,只有满足行过滤表达式的数据才会复制到订阅端。

如果订阅包含多个发布,同一张表在各发布中使用不同WHERE子句,那么满足任一表达式的行都会被复制。详情参见第29.4.6节。

警告

初始同步复制已有表数据时,不考虑publish参数,因此可能复制某些在DML复制阶段不会复制的行。参见第29.9.1节,以及第29.2.2节的示例。

注意

如果订阅端版本低于15,复制已有数据时不会使用行过滤器,即便发布端已定义。因为旧版本只能复制整张表的数据。

29.4.6 合并多个行过滤器

如果订阅包含多个发布,同一张表针对相同publish操作定义了不同过滤器,这些表达式会进行逻辑或组合。因此,满足任一表达式的行都会复制。以下任一情况成立时,同表的其他行过滤器都会变得多余:

  • 某个发布没有行过滤器。

  • 某个发布通过FOR ALL TABLES创建;此子句不允许行过滤器。

  • 某个发布通过FOR TABLES IN SCHEMA创建,且该表属于指定模式;此子句不允许行过滤器。

29.4.7 示例

先创建后续示例使用的表。


/* pub # */ CREATE TABLE t1(a int, b int, c text, PRIMARY KEY(a,c));
/* pub # */ CREATE TABLE t2(d int, e int, f int, PRIMARY KEY(d));
/* pub # */ CREATE TABLE t3(g int, h int, i int, PRIMARY KEY(g));

创建几个发布。p1只包含t1,并为t1定义行过滤器。p2包含两张表,t1没有过滤器、t2有过滤器。p3也包含两张表,二者都有过滤器。


/* pub # */ CREATE PUBLICATION p1 FOR TABLE t1 WHERE (a > 5 AND c = 'NSW');
/* pub # */ CREATE PUBLICATION p2 FOR TABLE t1, t2 WHERE (e = 99);
/* pub # */ CREATE PUBLICATION p3 FOR TABLE t2 WHERE (d = 10), t3 WHERE (g = 10);

可以使用psql查看各个发布已定义的行过滤表达式。


/* pub # */ \dRp+
                                         Publication p1
  Owner   | All tables | Inserts | Updates | Deletes | Truncates | Generated columns | Via root
----------+------------+---------+---------+---------+-----------+-------------------+----------
 postgres | f          | t       | t       | t       | t         | none              | f
Tables:
    "public.t1" WHERE ((a > 5) AND (c = 'NSW'::text))

                                         Publication p2
  Owner   | All tables | Inserts | Updates | Deletes | Truncates | Generated columns | Via root
----------+------------+---------+---------+---------+-----------+-------------------+----------
 postgres | f          | t       | t       | t       | t         | none              | f
Tables:
    "public.t1"
    "public.t2" WHERE (e = 99)

                                         Publication p3
  Owner   | All tables | Inserts | Updates | Deletes | Truncates | Generated columns | Via root
----------+------------+---------+---------+---------+-----------+-------------------+----------
 postgres | f          | t       | t       | t       | t         | none              | f
Tables:
    "public.t2" WHERE (d = 10)
    "public.t3" WHERE (g = 10)

还可以用psql查看每张表已定义的过滤表达式。t1属于两个发布,但只在p1中有过滤器;t2也属于两个发布,且各自的过滤器不同。


/* 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"

/* 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)

/* 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订阅。


/* sub # */ CREATE TABLE t1(a int, b int, c text, PRIMARY KEY(a,c));
/* sub # */ CREATE SUBSCRIPTION s1
/* sub - */ CONNECTION 'host=localhost dbname=test_pub application_name=s1'
/* sub - */ PUBLICATION p1;

插入一些行。只有满足p1中t1的WHERE子句的行会复制。


/* pub # */ INSERT INTO t1 VALUES (2, 102, 'NSW');
/* pub # */ INSERT INTO t1 VALUES (3, 103, 'QLD');
/* pub # */ INSERT INTO t1 VALUES (4, 104, 'VIC');
/* pub # */ INSERT INTO t1 VALUES (5, 105, 'ACT');
/* pub # */ INSERT INTO t1 VALUES (6, 106, 'NSW');
/* pub # */ INSERT INTO t1 VALUES (7, 107, 'NT');
/* pub # */ INSERT INTO t1 VALUES (8, 108, 'QLD');
/* pub # */ INSERT INTO t1 VALUES (9, 109, 'NSW');

/* 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)

/* sub # */ SELECT * FROM t1;
 a |  b  |  c
---+-----+-----
 6 | 106 | NSW
 9 | 109 | NSW
(2 rows)

更新一些数据,旧行和新行都满足p1中t1的WHERE子句,UPDATE会正常复制该变更。


/* pub # */ UPDATE t1 SET b = 999 WHERE a = 6;

/* 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)

/* sub # */ SELECT * FROM t1;
 a |  b  |  c
---+-----+-----
 9 | 109 | NSW
 6 | 999 | NSW
(2 rows)

更新一些数据,旧行不满足p1中t1的WHERE子句,而新行满足。UPDATE转换为INSERT并复制变更;可在订阅端看到新增行。


/* pub # */ UPDATE t1 SET a = 555 WHERE a = 2;

/* 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)

/* sub # */ SELECT * FROM t1;
  a  |  b  |  c
-----+-----+-----
   9 | 109 | NSW
   6 | 999 | NSW
 555 | 102 | NSW
(3 rows)

更新一些数据,旧行满足p1中t1的WHERE子句,而新行不满足。UPDATE转换为DELETE并复制变更;可看到订阅端已移除该行。


/* pub # */ UPDATE t1 SET c = 'VIC' WHERE a = 9;

/* 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)

/* sub # */ SELECT * FROM t1;
  a  |  b  |  c
-----+-----+-----
   6 | 999 | NSW
 555 | 102 | NSW
(2 rows)

以下示例展示,publish_via_partition_root如何决定分区表使用父表还是子表的行过滤器。

在发布端创建分区表。


/* pub # */ CREATE TABLE parent(a int PRIMARY KEY) PARTITION BY RANGE(a);
/* pub # */ CREATE TABLE child PARTITION OF parent DEFAULT;

在订阅端创建相同的表。


/* sub # */ CREATE TABLE parent(a int PRIMARY KEY) PARTITION BY RANGE(a);
/* sub # */ CREATE TABLE child PARTITION OF parent DEFAULT;

创建发布p4并订阅它,设置publish_via_partition_root为true。根分区表parent和分区child都定义了行过滤器。


/* pub # */ CREATE PUBLICATION p4 FOR TABLE parent WHERE (a < 5), child WHERE (a >= 5)
/* pub - */ WITH (publish_via_partition_root=true);

/* sub # */ CREATE SUBSCRIPTION s4
/* sub - */ CONNECTION 'host=localhost dbname=test_pub application_name=s4'
/* sub - */ PUBLICATION p4;

直接向parent和child插入一些值。因为publish_via_partition_root为true,复制使用parent的过滤器。


/* pub # */ INSERT INTO parent VALUES (2), (4), (6);
/* pub # */ INSERT INTO child VALUES (3), (5), (7);

/* pub # */ SELECT * FROM parent ORDER BY a;
 a
---
 2
 3
 4
 5
 6
 7
(6 rows)

/* sub # */ SELECT * FROM parent ORDER BY a;
 a
---
 2
 3
 4
(3 rows)

重复同样的测试,但把publish_via_partition_root设为false,并在分区child上定义过滤器。


/* pub # */ DROP PUBLICATION p4;
/* pub # */ CREATE PUBLICATION p4 FOR TABLE parent, child WHERE (a >= 5)
/* pub - */ WITH (publish_via_partition_root=false);

/* sub # */ ALTER SUBSCRIPTION s4 REFRESH PUBLICATION;

在发布端执行与前面相同的插入。因为publish_via_partition_root为false,复制使用child的过滤器。


/* pub # */ TRUNCATE parent;
/* pub # */ INSERT INTO parent VALUES (2), (4), (6);
/* pub # */ INSERT INTO child VALUES (3), (5), (7);

/* pub # */ SELECT * FROM parent ORDER BY a;
 a
---
 2
 3
 4
 5
 6
 7
(6 rows)

/* sub # */ SELECT * FROM child ORDER BY a;
 a
---
 5
 6
 7
(3 rows)

正文相关链接

来源与许可

原文:行过滤器。作者/维护者:PostgreSQL Global Development Group。

PostgreSQL License。本页为中文翻译或中文整理,保留原始示例;措辞与排版有调整。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容