这篇PostgreSQL逻辑复制文章够用了吧?

这篇PostgreSQL逻辑复制文章够用了吧?

Whoami:5年+金融、政府、医疗领域工作经验的DBA
Certificate:OCP、PCP
Skill:Oracle、Mysql、PostgreSQL
Platform:CSDN、墨天伦、公众号(呆呆的私房菜)

业务范围:数据库安装部署、日常维护、主备切换、故障处理、性能优化、技术培训等。

需要的伙伴或者商业合作请移步 公众号【呆呆的私房菜】获取联系方式。
阅读本文可以了解到逻辑复制概念、复制原理、应用场景、使用限制、实战案例以及使用过程中的常见问题及处理方式等内容。

01 逻辑复制概念

逻辑复制的历史:

2014年发布的PostgreSQL 9.4版本开始就支持逻辑复制了,但是没有将其引入内核。

逻辑复制是PostgreSQL 10版本重量级新特性,支持内置的逻辑复制;

在PostgreSQL 10之前的版本,虽然没有内置的逻辑复制,但是也可以通过其他方式实现(如触发器、自定义脚本等实现表级别同步,还有外部工具londsite3实现)。

逻辑复制是什么?

逻辑复制是一种基于数据对象的复制标识(通常是主键)复制数据对象及其更改的方法。

逻辑复制使用一种发布和订阅模型,其中有一个或多个订阅者订阅一个发布者节点上的一个或多个发布。订阅者从他们所订阅的发布拉取数据并且可能后续重新发布这些数据以允许级联复制或者更复杂的配置。

逻辑复制和物理复制都是基于wal日志实现的,同一个数据库实例可以同时使用逻辑复制和物理复制。

02 逻辑复制原理

PostgreSQL的逻辑复制基于wal日志和复制槽的机制。

主节点将其publication中的表的wal日志进行解析后,形成一种特殊的日志流,传给从节点。

从节点上的subscription对这些日志流进行解析重返,从而达到同步表数据的功能。

两者wal的传输是通过各自服务器上的wal sender和wal receiver进程来完成的。

注意:逻辑复制它复制的是SQL操作的结果,而不是SQL本身。

复制槽:提供了一种自动化的方法来确保主库在所有的备库收到wal记录之前不会移除它们,并且主库也不会移除可能导致恢复冲突的行,即使备库断开也是如此。

csharp 复制代码
select * from pg_replication_slots;

注意:逻辑复制的功能也在不断地完善,pg16版本目前已经支持双向复制了。

03 发布订阅配置

  1. 发布端:
ini 复制代码
# 设置日志模式
wal_level=logical
# 指定复制槽数
max_replication_slots
# 设置wal日志发送进程数量
max_wal_senders
  1. 订阅端:
bash 复制代码
# 指定逻辑复制工作的最大数据量,默认是4
max_logical_replication_workers
# 指定每个订阅最大同步工作者数量,默认是2
max_sync_workers_per_subscriptio
# 指定工作进程数
max_worker_processes

04 发布和订阅

1. 发布:

可以在任何物理复制主机上定义发布,定义发布的节点称为发布者;

每个发布只存在于一个数据库中;

发布者是一个表或一组表中生成的一组更改,也可能被描述为更改集或者复制集;

发布与schema不同,不影响表格的访问方式。如果需要,每张表可以添加到多个发布;

发布可以选择将它们所产生的改变限制在insert/update/delete的任意组合上,类似于触发器。默认情况下复制所有操作类型。
2. 订阅:

订阅是逻辑复制的下游端,定义订阅的节点称为订阅者;

订阅定义了与另一个数据库的连接以及它想要订阅的一个或一组发布;

订阅者数据库可以通过定义自己的发布来作为其他数据库的发布者;一个订阅者节点可以有多个订阅;

每个订阅都将通过一个复制槽接受更改;

逻辑复制订阅可以作为同步复制的备用数据库。

下面我们看看订阅发布的常用命令:

sql 复制代码
# 发布订阅实例
1. 创建一个发布,发布两个表中的所有修改
create publication p1 for table t1, t2;

2. 创建一个发布,发布所有表中的所有更改
create publication allp for all tables;

3. 创建一个发布,只发布一个表中的insert操作
create publication insertp for table t3 with (publish='insert');

4. 将发布修改为只发布删除和修改
alter publication noinsertp set (publish='update,delete');

5. 给发布添加一些表
alter publication p1 add table t4, t5;

6. 查看发布
select * from pg_publicatin;

# 订阅常用命令
1. 创建一个到远程服务器的订阅,复制发布p1和insertp中的表(即t1,t2,t3),并在提交时立即复制
create subscription s1 
connection 'host=10.0.0.21 port=5432 user=test dbname=testdb' 
publication p1, insertp;

2. 创建一个到远程服务器的订阅,复制发布insertp中的表,并且不开始复制直到启用复制
create subscription s2
connection 'host=10.0.0.21 port=5432 user=test dbname=testdb' 
publication insertp with (enabled=false);

3. 更改订阅发的发布:
alter subscription s1 set publication insertp;

4. 停止订阅
alter subscription s1 disable;

05 相关限制

原生逻辑复制使用限制:

不支持ddl复制(create table / alter table);

不支持temporary表和unlogged表复制;

不支持sequences复制(serial / bigserial / identity);

不支持大对象复制(bytea);

不支持视图、物化视图、外部表复制。

pglogical逻辑复制插件使用限制:

数据库版本限制:发布和订阅节点需要运行 PostgreSQL 9.4+;

复制源过滤和冲突检测需要 PostgreSQL 9.5+。

06 逻辑复制案例

sql 复制代码
一、环境说明
1. 发布端:
postgres=# select version();
                                                 version                                                 
---------------------------------------------------------------------------------------------------------
 PostgreSQL 16.3 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44), 64-bit
(1 行记录)

2. 订阅端:
postgres=# select version();
                                                 version                                                 
---------------------------------------------------------------------------------------------------------
 PostgreSQL 16.3 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 4.8.5 20150623 (Red Hat 4.8.5-44), 64-bit
(1 行记录)

二、环境准备
1. 发布端配置参数
vi $PGDATA/postgresql.conf
listen_addresses = '*'
wal_level = logical
max_replication_slots=10

2. 订阅端配置参数
vi $PGDATA/postgresql.conf
max_logical_replication_workers=2
max_sync_workers_per_subscription=2

3. 创建同步数据库和用户
create user repl replication login connection limit 10 encrypted password 'repl';

4. 模拟业务用户和表
create user test;
create database testdb owner test;
create schema testdb authorization test;
# 创建表1
create table testdb.t1 (id int);
insert into testdb.t1 values (1),(2),(3);

5. 授予同步用户权限
grant connect on database testdb to repl;
grant usage on schema testdb to repl;
grant select on testdb.t1 to repl;
\dp+ t1;


三、创建发布
testdb=> create publication p1 for table testdb.t1;
CREATE PUBLICATION
testdb=> select * from pg_publication;
  oid  | pubname | pubowner | puballtables | pubinsert | pubupdate | pubdelete | pubtruncate | pubviaroot 
-------+---------+----------+--------------+-----------+-----------+-----------+-------------+------------
 16430 | p1      |    16413 | f            | t         | t         | t         | t           | f
(1 行记录)

四、创建订阅
1. 创建接收表
create user test;
create database testdb owner test;
create schema testdb authorization test;
create table testdb.t1 (id int);

2. 创建订阅
postgres=# create subscription s1 connection 'host=10.28.12.21 port=5432 dbname=testdb user=repl' publication p1;
NOTICE:  created replication slot "s1" on publisher
CREATE SUBSCRIPTION

postgres=# select * from pg_subscription;
  oid  | subdbid | subskiplsn | subname | subowner | subenabled | subbinary | substream | subtwophasestate | subdisableonerr | subpasswordrequired | subrunasowner |                    subconninfo                     | subslotname | subs
ynccommit | subpublications | suborigin 
-------+---------+------------+---------+----------+------------+-----------+-----------+------------------+-----------------+---------------------+---------------+----------------------------------------------------+-------------+-----
----------+-----------------+-----------
 16397 |       5 | 0/0        | s1      |       10 | t          | f         | f         | d                | f               | t                   | f             | host=10.28.12.21 port=5432 dbname=testdb user=repl | s1          | off 
          | {p1}            | any
(1 row)

五、测试同步
1. 发布端:
testdb=> select * from testdb.t1;
 id 
----
  1
  2
  3
(3 行记录)

testdb=> insert into testdb.t1 values (4);
INSERT 0 1

2. 目标端
postgres=# select * from testdb.t1;
 id 
----
  1
  2
  3
  4
(4 rows)

07 常见问题及解决方案

🔺failover slot:生产库一般都会构建主备架构,主库故障后,从库接管服务了,但是却没有相应的复制槽信息,也就是缺少failover slot。这是由于保存slot信息的物理文件未同步到备库。

bash 复制代码
解决方案:
1. 主库创建复制槽,检查备库wal文件是否连续;
2. 主库复制$PGDATA/pg_repslot包含slot信息的物理文件到备库;
3. 备库重启,重启后才可以看到复制槽信息,原因是读取slot物理文件的函数只会在postmaster进程启动时调用;
4. 定期查询主库slot状态,使用pg_replication_slot_advance函数推进备库复制槽

🔺ddl同步:原生的逻辑复制不支持复制ddl语句。

markdown 复制代码
解决方案:使用事务触发器来处理
1. 使用事件触发器记录变动的表结构,记录到ddl_change表中,并将该表通过逻辑复制进行发布;
2. 订阅端接收到该表的数据变更后,处理为相应的ddl语句去执行。

MARKDOWN 复制 全屏

🔺toast问题:

markdown 复制代码
解决方案:
1. 使用占位符的方式处理,订阅端接收到占位符就不对这一列进行处理,过程比较麻烦。

🔺wal积压问题:复制槽记录的xmin是全局的,当发布端的表一直没更新时,xmin没有推进会导致wal积压。

markdown 复制代码
解决方案:
1. 创建一张心跳表,周期性写入数据并发布,推进xmin,防止wal堆积。

🔺大事务延迟:事务在commit之后才会进行解析,对于大事务来说会导致延迟。

🔺双向同步(PostgreSQL 16版本已经支持)。

相关推荐
桥Dopey14 分钟前
关系型数据库PostgreSQL for Mac 保姆级使用教程
数据库·postgresql
呆呆的私房菜7 小时前
PostgreSQL高可用之流复制架构
postgresql
战族狼魂7 小时前
基于SpringBoot+PostgreSQL+ROS Java库机器人数据可视化管理系统
java·spring boot·postgresql
拿破轮21 小时前
查询Hologres或postgresql中的数据
数据库·postgresql
松树戈1 天前
PostgreSQL使用LIKE右模糊没有走索引分析&验证
数据库·postgresql
文牧之1 天前
PostgreSQL 常用日志
运维·数据库·postgresql
qq_441996052 天前
为何 RAG 向量存储应优先考虑 PostgreSQL + pgvector 而非 MySQL?
数据库·mysql·postgresql
阿里小阿希2 天前
解决 Spring Boot + MyBatis 项目迁移到 PostgreSQL 后的数据类型不匹配问题
spring boot·postgresql·mybatis
松树戈3 天前
PostgreSQL 分区表——范围分区SQL实践
数据库·sql·postgresql