PG数据库逻辑复制比较大的一个弊端就是它自身无法满足同步对象DDL操作的同步,如果单纯依赖原生的逻辑复制架构,逻辑复制表对象表结构变更操作的整套维护操作是及其复杂、而且容易遗漏的。目前比较常见的解决方案是利用触发器抓取主库相关表对象的DDL变更,然后连接逻辑复制订阅端进行同步应用,本文主要讲解一个可直接部署使用的逻辑复制DDL同步插件,简化我们的运维成本。
一、逻辑复制+DDL同步部署
1.1 必要环境准备
- 数据库环境 : PostgreSQL 13
- 逻辑复制数据库账号:logical_repl
- 业务执行DDL操作数据库账号:app_user
1.2 源端逻辑复制发布
1、手动在逻辑复制发布端创建两张基础表,模拟t1、t2表进行逻辑复制
test=# \d
List of relations
Schema | Name | Type | Owner
--------+------+-------+----------
schem1 | t1 | table | postgres
schem1 | t2 | table | postgres
(2 rows)
test=# \d t1
Table "schem1.t1"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | not null |
Indexes:
"t1_pkey" PRIMARY KEY, btree (id)
Publications:
"pub1"
test=# \d t2
Table "schem1.t2"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | not null |
Indexes:
"t2_pkey" PRIMARY KEY, btree (id)
Publications:
"pub1"
2、创建逻辑复制用户并授权
1)用户创建
postgres=# create user logical_repl with password 'xxx';
postgres=# alter user logical_repl replication;
2)配置文件
-- 修改配置文件
$ vim pg_hba.conf
host all logical_repl 0.0.0.0/0 md5
host replication logical_repl 0.0.0.0/0 md5
-- 重新加载配置文件
postgres=# select pg_reload_conf();
3)授权对应逻辑复制表对象的权限
\c test
set search_path='schem1';
GRANT USAGE ON SCHEMA schem1 to logical_repl;
GRANT SELECT ON ALL TABLES IN SCHEMA schem1 TO logical_repl;
ALTER DEFAULT PRIVILEGES IN SCHEMA schem1 grant select on tables to logical_repl;
3、配置发布通道
test=# create publication pub1;
CREATE PUBLICATION
test=# alter publication pub1 add table t1;
ALTER PUBLICATION
test=# alter publication pub1 add table t2;
ALTER PUBLICATION
test=# select * from pg_publication_tables ;
pubname | schemaname | tablename
---------+------------+-----------
pub1 | schem1 | t1
pub1 | schem1 | t2
(2 rows)
1.2 目标端逻辑复制订阅
1、在创建订阅任务之前提前将需要进行逻辑复制的表对象进行创建(可以利用pg_dum进行初始化表结构的同步)
test1=# \d
Did not find any relations.
test1=# create table t1(id int primary key);
CREATE TABLE
test1=# create table t2(id int primary key);
CREATE TABLE
test1=# \d
List of relations
Schema | Name | Type | Owner
--------+------+-------+----------
schem1 | t1 | table | postgres
schem1 | t2 | table | postgres
(2 rows)
2、创建逻辑复制的订阅任务
test1=# create subscription sub1 connection 'host=172.16.104.55 port=5433 dbname=test user=logical_repl password=123' publication pub1;
NOTICE: created replication slot "sub1" on publisher
CREATE SUBSCRIPTION
1.3 pgl_ddl_deploy 插件部署使用
1、pgl_ddl_deploy安装部署(发布/订阅端都需要安装)
-- 软件下载地址
https://github.com/enova/pgl_ddl_deploy
-- 安装部署
# tar xf pgl_ddl_deploy-2.1.0.tar.gz -C /usr/local/postgresql-13.4/contrib/
# cd /usr/local/postgresql-13.4/contrib/pgl_ddl_deploy-2.1.0/
# make && make install
2、发布端配置相关DDL同步设置
1)加载插件
test=# CREATE EXTENSION pgl_ddl_deploy;
CREATE EXTENSION
2)编写配置文件,该配置文件表示会复制schem1库中所有的DDL操作
本案例中是配置了将发布端schem1下所有对象的变更均同步至逻辑复制的订阅端,数据库的DML同步依赖PG原生的逻辑复制。
-- 编辑DDL同步配置设置
test=# INSERT INTO pgl_ddl_deploy.set_configs (set_name,include_schema_regex,driver) VALUES('pub1','schem1','native'::pgl_ddl_deploy.driver);
INSERT 0 1
- 参数说明:
- pub1 : 发布端发布通道名称
- schema1 : 可指定schema级别的DDL同步,本案例只对schema1进行同步
- 'native'::pgl_ddl_deploy.driver : 表示DML同步使用PG原生的逻辑复制
-- 配置生效
test=# SELECT pgl_ddl_deploy.deploy('pub1') from pgl_ddl_deploy.set_configs;
NOTICE: table "tmp_objs" does not exist, skipping
NOTICE: event trigger "auto_rep_ddl_create_1_pub1" does not exist, skipping
NOTICE: event trigger "auto_rep_ddl_drop_1_pub1" does not exist, skipping
NOTICE: event trigger "auto_rep_ddl_unsupp_1_pub1" does not exist, skipping
NOTICE: function pgl_ddl_deploy.auto_rep_ddl_create_1_pub1() does not exist, skipping
NOTICE: function pgl_ddl_deploy.auto_rep_ddl_drop_1_pub1() does not exist, skipping
NOTICE: function pgl_ddl_deploy.auto_rep_ddl_unsupp_1_pub1() does not exist, skipping
deploy
--------
t
(1 row)
3)权限加载
若需要使用指定账号执行该schema下的DDL操作,需要将该数据库账号对pgl_ddl_deploy进行授权。
test=# SELECT pgl_ddl_deploy.add_role(oid) from pg_roles where rolname='logical_repl';
add_role
----------
t
(1 row)
test=# SELECT pgl_ddl_deploy.add_role(oid) from pg_roles where rolname='app_user';
add_role
----------
t
(1 row)
3、订阅端加载pgl_ddl_deploy插件并授权
1)若为全新的环境,可以先安装插件后创建逻辑复制订阅任务
test1=# create extension pgl_ddl_deploy ;
CREATE EXTENSION
test1=# create subscription sub1 connection 'host=172.16.104.55 port=5433 dbname=test user=logical_repl password=123' publication pub1;
NOTICE: created replication slot "sub1" on publisher
CREATE SUBSCRIPTION
2)若逻辑复制已经创建逻辑复制订阅任务已经创建,在安装插件后重新加载逻辑复制任务
test1=# create extension pgl_ddl_deploy ;
CREATE EXTENSION
test1=# alter subscription sub2 refresh publication ;
ALTER SUBSCRIPTION
3)若需要使用指定账号执行该schema下的DDL操作,需要将该数据库账号对pgl_ddl_deploy进行授权。
test=# SELECT pgl_ddl_deploy.add_role(oid) from pg_roles where rolname='logical_repl';
add_role
----------
t
(1 row)
test=# SELECT pgl_ddl_deploy.add_role(oid) from pg_roles where rolname='app_user';
add_role
----------
t
(1 row)
二、功能验证
1、表对象DDL同步验证
源 目标
--------------------------------------------------------------------------------------------------------------------------------------
test1=# select * from t1;
id
----
(0 rows)
test=# insert into t1 values(1);
INSERT 0 1
test1=# select * from t1; // 正常的数据复制
id
----
1
(1 row)
test=# alter table t1 add id1 int; // 逻辑复制相关表的DDL复制
ALTER TABLE
test1=# \d t1
Table "schem1.t1"
Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+---------
id | integer | | not null |
id1 | integer | | |
Indexes:
"t1_pkey" PRIMARY KEY, btree (id)
test=# create table t3 (id int); // 非逻辑复制表,在当前schema下,也会同步自目标库,但是不影响原有的逻辑复制表清单
CREATE TABLE
test=# insert into t3 values(1);
INSERT 0 1
test1=# \d
test=# select * from pg_publication_tables ; List of relations
pubname | schemaname | tablename Schema | Name | Type | Owner
---------+----------------+----------- --------+------+-------+----------
pub1 | schem1 | t1 schem1 | t1 | table | postgres
pub1 | schem1 | t2 schem1 | t2 | table | postgres
pub1 | pgl_ddl_deploy | queue schem1 | t3 | table | postgres
(3 rows) (3 rows)
test1=# select * from t3;
id
----
(0 rows)
// 模拟将一些无主键表删除复制的情况
test=# create table t4(id int);
CREATE TABLE
test=# insert into t4 values(1);
INSERT 0 1
test=# alter publication pub1 add table t4;
ALTER PUBLICATION
test1=# alter subscription sub1 refresh publication ;
ALTER SUBSCRIPTION
test1=# select * from t4;
id
----
1
(1 row)
2、表对象移除逻辑复制后,数据不再进行同步,但表结构同步依旧同步
test=# delete from t4 where id=1; // 由于无主键或者唯一键,无法执行delete、update
ERROR: cannot delete from table "t4" because it does not have a replica identity and publishes deletes
HINT: To enable deleting from the table, set REPLICA IDENTITY using ALTER TABLE.
test=# alter publication pub1 drop table t4; // 将无主键表清除在逻辑复制之外
ALTER PUBLICATION
test=# delete from t4 where id=1;
DELETE 1
test1=# select * from t4;
id
----
1
(1 row)
test=# insert into t4 values(1);
INSERT 0 1
test1=# select * from t4;
id
----
1
(1 row)
test=# alter table t4 add column id1 int;
ALTER TABLE
test=# \d t4 test1=# \d t4
Table "schem1.t4" Table "schem1.t4"
Column | Type | Collation | Nullable | Default Column | Type | Collation | Nullable | Default
--------+---------+-----------+----------+--------- --------+---------+-----------+----------+---------
id | integer | | | id | integer | | |
id1 | integer | | | id1 | integer | | |
3、删除逻辑复制表对象验证
test=# select * from pg_publication_tables ;
pubname | schemaname | tablename
---------+----------------+-----------
pub1 | schem1 | t1
pub1 | schem1 | t2
pub1 | pgl_ddl_deploy | queue
(3 rows)
test=# drop table t2;
DROP TABLE
test=# select * from pg_publication_tables ;
pubname | schemaname | tablename
---------+----------------+-----------
pub1 | schem1 | t1
pub1 | pgl_ddl_deploy | queue
(2 rows)
test=# \d test1=# \d
List of relations List of relations
Schema | Name | Type | Owner Schema | Name | Type | Owner
--------+------+-------+---------- --------+------+-------+----------
schem1 | t1 | table | postgres schem1 | t1 | table | postgres
schem1 | t3 | table | postgres schem1 | t3 | table | postgres
schem1 | t4 | table | postgres schem1 | t4 | table | postgres
(3 rows) (3 rows)
总结:
- pgl_ddl_deploy基本可满足大部分场景的DDL同步,同步粒度可从配置文件着手进行优化,目前测试来看create index是无法进行同步的,需要手动在逻辑订阅端执行;
- 若同步整个schema级别的表对象DDL
- 发布端新增表架构自动同步至逻辑复制订阅端,且会将该表加入至逻辑复制对象,需要订阅端重新加载订阅通道开始进行数据同步
- 发布端删除逻辑复制对象,数据DML不再继续同步,但是表结构DDL操作依旧会进行同步
- 发布端删除表后,会自动删除其逻辑复制对象
参考文档:
https://github.com/enova/pgl_ddl_deploy
https://developer.aliyun.com/article/277983