
前言
昨天我们安装了PostgreSQL 13逻辑复制,今天我们来详细介绍一下它的原理。
逻辑复制的体系结构
逻辑复制体系结构如下图,来自于Noriyoshi Shinoda的Logical Replication Internals:

wal sender进程
逻辑解码插件pgoutput对WAL文件进行逻辑解码,然后wal sender进程将解码后的消息发送到订阅端。在发布实例进程如下所示:

如果有多个订阅进程,则会对应启动多个wal sender进程。例如我在从库再发起一个订阅。
--从库执行
hr=> CREATE TABLE jobs2 (
hr(> job_id varchar(10) NOT NULL,
hr(> job_title varchar(35) NOT NULL,
hr(> min_salary integer,
hr(> max_salary integer
hr(> ) ;
CREATE TABLE
hr=# create subscription testsub2 connection 'host=192.168.56.119 port=5432 dbname=hr user=replication' publication testpub;
CREATE SUBSCRIPTION
此时查看主库可以发现wal sender进程为2个。

logical replication launcher进程
logical replication launcher进程,在发布实例和订阅实例都存在。主要的作用就是启动逻辑复制工作进程。
logical replication worker for subscription进程
只在订阅实例上存在,每一个订阅都会启动一个对应的进程,它和wal sender进程进行连接,接收wal sender传输过来的信息并更新对应的表。

创建PUBLICATION之后
当我们创建PUBLICATION
之后,默认会将信息存储在pg_publication
视图中。其中包含发布的名字,哪些类型的DML语句能被复制。puballtables
字段如果为true,则代表包括数据库中的所有表,包括将来将要创建的任何表。
hr=> select * from pg_publication;
-[ RECORD 1 ]+--------
oid | 57510
pubname | testpub
pubowner | 57496
puballtables | f
pubinsert | t
pubupdate | t
pubdelete | t
pubtruncate | t
pubviaroot | f
另外一个视图pg_publication_rel
存储着表的oid和PUBLICATION的oid。如果指定了FOR ALL TABLES
选项,则此表中不存储任何内容。
hr=> select * from pg_publication_rel;
-[ RECORD 1 ]--
oid | 57511
prpubid | 57510
prrelid | 57504
视图pg_publication_tables
存储了表的名字、schema和发布的名称。
-[ RECORD 1 ]-------
pubname | testpub
schemaname | public
tablename | jobs
创建SUBSCRIPTION之后
当创建了SUBSCRIPTION
之后,默认信息存储在pg_subscription
视图中。该视图存储了连接到发布端的信息,复制插槽的名字,已经连接上的发布的名字。使用同步复制还是异步复制,当前可以看到都是异步复制。
hr=# select * from pg_subscription;
-[ RECORD 1 ]---+---------------------------------------------------------
oid | 16392
subdbid | 16385
subname | testsub
subowner | 10
subenabled | t
subconninfo | host=192.168.56.119 port=5432 dbname=hr user=replication
subslotname | testsub
subsynccommit | off
subpublications | {testpub}
-[ RECORD 2 ]---+---------------------------------------------------------
oid | 32771
subdbid | 16385
subname | testsub2
subowner | 10
subenabled | t
subconninfo | host=192.168.56.119 port=5432 dbname=hr user=replication
subslotname | testsub2
subsynccommit | off
subpublications | {testpub}
订阅连接到发布实例之后,会做以下几件事:
检查连接用户,是否具备复制权限。
在订阅侧创建逻辑复制插槽,插槽的名字默认和订阅名字保持一致。
它不会检查发布是否存在。
在
pg_subscription_rel
表中注册复制目标表。
hr=# select * from pg_subscription_rel;
-[ RECORD 1 ]---------
srsubid | 16392
srrelid | 16386
srsubstate | r
srsublsn | 0/1D8DDB0
-[ RECORD 2 ]---------
srsubid | 32771
srrelid | 16386
srsubstate | r
srsublsn | 0/1DC2D30
同步初始化数据。
订阅实例如果存在数据,它不会删除。
下图时初始化同步数据的过程。

可以看到第3和第4步,它会创建一个临时的插槽,并拷贝全部的数据过去。而增量的数据则继续通过之前的插槽传输。
后记
未完,待续。。。
参考链接
1.https://www.slideshare.net/noriyoshishinoda/pgconfasia-2017-logical-replication-internals-english?from_action=save


励志成为PostgreSQL大神
长按关注吧




