由 John Doe 七月 9, 2025
想发布和订阅数据库中某个模式下的表吗?PostgreSQL 可以一键发布单个模式中的表了。
特性提交日志
允许发布模式中的表。
CREATE/ALTER PUBLICATION 中新增了 “FOR ALL TABLES IN SCHEMA” 选项,允许指定一个或多个模式,发布者会选择这些模式中的表向订阅者发送数据。
新语法允许同时指定表和模式。例如:
CREATE PUBLICATION pub1 FOR TABLE t1,t2,t3, ALL TABLES IN SCHEMA s1,s2;
或者
ALTER PUBLICATION pub1 ADD TABLE t1,t2,t3, ALL TABLES IN SCHEMA s1,s2;
新增了系统表 pg_publication_namespace,用于维护用户希望通过发布所发布的模式。修改了输出插件(pgoutput),以便在关系属于模式发布的一部分时发布更改。
更新了 pg_dump,以识别和转储模式的发布。更新了\d
系列命令以显示模式的发布,且\dRp+
命令现在也会显示相关的模式(如果有的话)。
讨论:https://www.postgresql.org/message-id/CALDaNm0OANxuJ6RXqwZsM1MSY4s19nuH3734j4a72etDwvBETQ@mail.gmail.com
示例
从 PostgreSQL 10 开始,我们就有了逻辑复制功能。从那以后,我们可以针对特定的一个或多个表,或者数据库中的所有表进行逻辑复制。
现在,我们在粒度上又进了一步,可以针对一个或多个模式中的所有表进行复制。
让我们看看它是如何工作的。为了测试,我们在同一台服务器上创建两个数据库,分别命名为 db_primary 和 db_replica:
$ psql -X -p 5430 -c 'create database db_primary'
$ psql -X -p 5431 -c 'create database db_replica'
然后,在每个数据库中,运行下面的 SQL 脚本:
CREATE TABLE public.alpha ( id int generated always as identity PRIMARY KEY, ts timestamptz, payload TEXT);
CREATE SCHEMA bob;
CREATE SCHEMA carol;
CREATE SCHEMA dan;
CREATE TABLE bob.echo ( id int generated always as identity PRIMARY KEY, ts timestamptz, payload TEXT);
CREATE TABLE bob.foxtrot ( id int generated always as identity PRIMARY KEY, ts timestamptz, payload TEXT);
CREATE TABLE carol.golf ( id int generated always as identity PRIMARY KEY, ts timestamptz, payload TEXT);
CREATE TABLE carol.hotel ( id int generated always as identity PRIMARY KEY, ts timestamptz, payload TEXT);
CREATE TABLE carol.india ( id int generated always as identity PRIMARY KEY, ts timestamptz, payload TEXT);
CREATE TABLE dan.juliett ( id int generated always as identity PRIMARY KEY, ts timestamptz, payload TEXT);
CREATE TABLE dan.kilo ( id int generated always as identity PRIMARY KEY, ts timestamptz, payload TEXT);
CREATE TABLE dan.lima ( id int generated always as identity PRIMARY KEY, ts timestamptz, payload TEXT);
CREATE TABLE dan.mike ( id int generated always as identity PRIMARY KEY, ts timestamptz, payload TEXT);
这会创建 3 个模式,以及 10 个结构相同但名称不同的表,分布在 4 个模式中(3 个是新创建的,还有 1 个表在 public 模式中)。
现在,让我们在 db_primary 中创建一个发布,其中包含 “bob” 模式中的所有表、“dan” 模式中的部分表,以及 public 模式中的一个表:
create publication test_pub for table dan.juliett, dan.kilo, public.alpha, all tables in schema bob;
db_primary=# \dRp+
Publication test_pub
Owner | All tables | Inserts | Updates | Deletes | Truncates | Via root
---------+------------+---------+---------+---------+-----------+----------
redrock | f | t | t | t | t | f
Tables:
"dan.juliett"
"dan.kilo"
"public.alpha"
Tables from schemas:
"bob"
一切就绪,现在让我们从副本数据库进行订阅:
create subscription test_sub connection 'host=127.0.0.1 port=5430 dbname=db_primary user=postgres' publication test_pub;
NOTICE: created replication slot "test_sub" on publisher
接下来,让我们进行测试:
insert into public.alpha (ts, payload) values (now(), 'initial value') returning *;
id | ts | payload
----+-------------------------------+---------------
1 | 2025-06-16 08:13:04.629613+08 | initial value
(1 row)
insert into public.alpha (ts, payload) values (now(), 'initial value') returning *;
id | ts | payload
----+-------------------------------+---------------
2 | 2025-06-16 08:13:15.073362+08 | initial value
(1 row)
update public.alpha set payload = 'second value' where id = 2;
select * from public.alpha;
id | ts | payload
----+-------------------------------+---------------
1 | 2025-06-16 08:13:04.629613+08 | initial value
2 | 2025-06-16 08:13:15.073362+08 | second value
(2 rows)
看看副本数据库中的情况:
select * from public.alpha;
id | ts | payload
----+-------------------------------+---------------
1 | 2025-06-16 08:13:04.629613+08 | initial value
2 | 2025-06-16 08:13:15.073362+08 | second value
(2 rows)
这很简单。那位于某个模式中的表呢?test_pub 包含了 bob 模式中的所有表,我们来看看:
insert into bob.echo (ts, payload) values (now(), 'xxx') returning *;
id | ts | payload
----+-------------------------------+---------
1 | 2025-06-16 08:15:50.809102+08 | xxx
(1 row)
insert into bob.foxtrot (ts, payload) values (now(), 'yyy') returning *;
id | ts | payload
----+-------------------------------+---------
1 | 2025-06-16 08:15:50.824428+08 | yyy
(1 row)
副本数据库中的情况:
select * from bob.echo;
id | ts | payload
----+-------------------------------+---------
1 | 2025-06-16 08:15:50.809102+08 | xxx
(1 row)
select * from bob.foxtrot;
id | ts | payload
----+-------------------------------+---------
1 | 2025-06-16 08:15:50.824428+08 | yyy
(1 row)
工作正常。接下来,让我们看看添加新表会发生什么。
$ psql -X -p 5430 -d db_primary -c "CREATE TABLE bob.november ( id int generated always as identity PRIMARY KEY, ts timestamptz, payload TEXT);"
$ psql -X -p 5431 -d db_replica -c "CREATE TABLE bob.november ( id int generated always as identity PRIMARY KEY, ts timestamptz, payload TEXT);"
完成上述操作后,让我们看看 november 表中的新数据是否会被复制:
$ psql -X -p 5430 -d db_primary -c "INSERT INTO bob.november (ts, payload) values (now(), 'cat') returning *"
id | ts | payload
----+-------------------------------+---------
1 | 2025-06-16 10:27:43.817067+08 | cat
(1 row)
验证一下:
$ psql -X -p 5431 -d db_replica -c "select * from bob.november"
id | ts | payload
----+----+---------
(0 rows)
为什么会这样呢?原因很简单。虽然发布中包含了所有数据,但订阅并不知道新表的存在。
要使其生效,我们需要在副本数据库上再执行一个操作:
alter subscription test_sub refresh publication;
然后,之前的数据立即就出现了:
select * from bob.november;
id | ts | payload
----+-------------------------------+---------
1 | 2025-06-16 10:27:43.817067+08 | cat
(1 row)
非常不错的特性,感谢所有参与的社区人员。
参考
提交日志:https://git.postgresql.org/pg/commitdiff/5a2832465fd8984d089e8c44c094e6900d987fcd